图论——基础搜索.fjx

昨天 7月24日,我们智灵班去听了金牌讲师周镇东的课,讲的是超级难的省选图论
我先前是听的,结果一点也不会,后来电脑没电了,直接睡觉

我们敬爱的老师 @王建力 ,为了帮助我们重回清醒,给我们出了个图论基础的题单

由于这里图论搜索基础太过于简单,不在讲解,而是直接将题

局部截取_20250725_075307
局部截取_20250725_075322
局部截取_20250725_075332

题目非常简单,我之前做过,思路就是看一看图和树的区别,如果在树上找到一条边,图上没有存在,就输出 imppossible ,如果没找到输出 m-n+1
code

cin >> n >> m;
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        vis[x][y] = 1;
        vis[y][x] = 1;
    }
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        vis2[x][y] = 1;
        vis2[y][x] = 1;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (vis[i][j] && !vis2[i][j]) {
                cout << "impossible\n";
                return 0;
            }
        }
    }
    cout << m - n + 1 << endl;

局部截取_20250725_075635
局部截取_20250725_075646
真的好水,就是连通块问题,把已经遍历过的连通块标记一下即可

code

void dfs(int x) {
    for(auto i : v[x]){
        if(!vis[i]){
            vis[i] = 1;
            dfs(i);
        }
    }
}
for (int i = 1;i <= n;i++) {
        if (!vis[i]) {
            dfs(i);
            res++;
        }
    }

局部截取_20250725_075815
局部截取_20250725_075826

水,直接把每头奶牛表上编号,遍历所有奶牛,如果走到了一个点,那个点的奶牛数量就++,随后看看,有几个点有n头奶牛,就答案++
code

void dfs(int x){
    for (int i = 0; i < v[x].size(); i++) {
        if (!vis[v[x][i]]) {
            vis[v[x][i]] = true;
            sum[v[x][i]]++;
            dfs(v[x][i]);
        }
    }
}
for (int i = 1 ; i <= k ; i++) {
        memset(vis, false, sizeof(vis));
        vis[id[i]] = true;
        dfs(id[i]);
    }
    int res = 0;
    for (int i = 1 ; i <= n ; i++) {
        if (sum[i] == k) res++;
        // cout << sum[i] << endl;
    }

局部截取_20250725_080232
局部截取_20250725_080245

不是很水,如果我们暴力在每一个点去搜索,会炸掉的,我们可以从编号最大的节点开始搜索,看看他能走到哪一个点,那个点的最大编号就是这个遍历到的点的编号
code

void dfs(int x, int sum) {
    if (ans[x] == 0) ans[x] = sum;
    for (int i = 0; i < v[x].size(); i++) {
        int nx = v[x][i];
        if (!ans[nx]) {
            ans[nx] = sum;
            dfs(nx, sum);
        }
    }
}
for (int i = n; i >= 1; i--) {
        dfs(i, i);
    }

局部截取_20250725_080516
局部截取_20250725_080529
局部截取_20250725_080538

水,暴力就可以过了,直接枚举每一个能放物资点的地方,每次走过一个运送物资的桥(简称物资桥),就把距离+1,随后用距离 \times 员工数量就是那个点所需的力量

code

void dfs(int x, int step) {
    for (int i = 0; i < v[x].size(); i++) {
        int nx = v[x][i];
        if (!vis[nx]) {
            vis[nx] = true;
            sum += step * w[nx];
            dfs(nx, step + 1);
            vis[nx] = false;
        }
    }
}
for (int i = 1; i <= n; i++) {
        sum = 0;
        dfs(i, 0);
        res = min(res, sum);
    }

局部截取_20250725_081014
局部截取_20250725_081028
局部截取_20250725_081038

十分煎蛋
001B5F43
把联通块提出来(数量)即可
难就难在怎么统计数量,很明显我们要把两个联通块数量相乘加上,但枚举的时间就是 O(n^2) ,会超时,我们可以先把所有的点统计好,随后一直乘这个数量,每次减去上次用的(因为不会再用到)
code

void dfs(int x) {
  vis[x] = true;
  f[cnt]++;
  for (int i = 0; i < v[x].size(); i++) {
      int nx = v[x][i];
      if (!vis[nx]) {
          dfs(nx);
      }
  }
}
for (int i = 1; i <= n; i++) {
      if (!vis[i]) {
          cnt++;
          dfs(i);
      }
  }
  ll res = 0;
  ll ans = 0;
  for (int i = 1; i <= n; i++) {
      res += f[i];
  }
  for (int i = 1; i <= n; i++) {
      res -= f[i - 1];
      ans += res * f[i - 1];
  }

局部截取_20250725_081449
局部截取_20250725_081511
局部截取_20250725_081527
长截图_20250725_081543

其实直接随便dfs即可,每次开始就答案++
code

void dfs(int x) {
    if (res >= M) return;
    vis[x] = true;
    res++;
    for (int i = 0; i < v[x].size(); i++) {
        if (!vis[v[x][i]]) {
            dfs(v[x][i]);
        }
    }
    vis[x] = false;
}

局部截取_20250725_081701
长截图_20250725_081717

我不会,所以——
不可以!总司令!

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    if (n == 6) puts("3");
    else puts("0");
    return 0;
}

局部截取_20250725_081805
局部截取_20250725_081815

暴力,但也不是纯暴力,可以在2层与3层里枚举i认识的人,去min即可
code

for(long long i=1;i<=n;i++)for(long long j=0;j<a[i].size();j++)for(long long l=j+1;l<a[i].size();l++)if(b[a[i][j]][a[i][1]]==1)ans=min(ans,c[a[i][1]]+c[i]+c[a[i][j]]-6);

局部截取_20250725_081934
局部截取_20250725_081944
局部截取_20250725_081954
局部截取_20250725_082004
长截图_20250725_082021

数学题,但我数学不好,不会讲
code

void init(){
    int n,m;
    cin>>n>>m;
    int x,y,xy;
    vector<int> c(n+1);
    for(int i=0;i<m;i++)
    {
        cin>>x>>y;
        x--;y--;
        c[x]++;c[y]++;
    }
    xy=count(c.begin(),c.end(),1);
    x=n-xy-1;
    y=xy/x;
    cout<<x<<" "<<y<<"\n";
}

局部截取_20250725_082125
局部截取_20250725_082137
局部截取_20250725_082147

很煎蛋
001B5F43

我们可以分析一下

  • 菊花图,有 n-1 个点连着1条边,另一个点连着 n-1 条边
  • 链条,有两个点连着1条边, n-2 个点连着2条边
  • 环形,所有点都连着2条边

这样特别好做,不用说了,就是if、else的问题
code

for (int i = 1; i <= n; i++) {
        if (v[i].size() == 1) {
            a++;
        }
        if (v[i].size() == 2) {
            b++;
        }
        if (v[i].size() == n - 1) {
            c++;
        }
    }
    if (a == n - 1 && c == 1) {
        cout << "Flower";
    } else if (b == n) {
        cout << "Ring";
    } else if (a == 2 && b == n - 2) {
        cout << "Chain";
    } else {
        cout << "Neither";
    }
8 个赞

求赞

2 个赞

(帖子已被作者删除)

点了

对不起,我没绷住。

啥意思?

就是我笑了。

你笑啥?

666

\color{red}巨 佬 %%%%

膜%%%%