题意:给定一个图,然后找出所有到剩余点距离最近的点。(我这个看不懂也可以看看题目叭,我觉得题目讲的很易懂的)
题解:首先这题,既然我们要找出所有的点,那肯定是要求出所有点到剩余点的距离的。那这里我们用sum[i]表示i到其他点的距离和,只需要求出这个sum数组,那么我们遍历一遍就可以出来结果了。同时我们要对这个图有一个基本认知,如果你看到最后面的备注里面(图的类型保证没有大小大于等于3的环)应该就可以明白这是一个树了。
那么这个sum数组怎么求呢,总不可能每个点跑一次bfs/dijkstra吧。这里我们就要利用这个每个边都是1的特性了。我们采用dfs维护距离和的方法,假设当前节点是sum[x],我们要转移到sum[y],就有这么一条转移方程 sum[y]=sum[x]-cnt[y]+(n-cnt[y]).这个cnt[y]代表的是y这个节点的子树重量。从x到y这一步,y这个节点到y的子节点的距离全部都-1 故-cnt[y],同时不是子树的点,都+1,所以+(n-cnt[y])。那到了这一步,我们需要求出cnt数组了。我们假设刚开始以1为根,然后dfs回溯的时候算出cnt。最后再来一次dfs维护距离和,求出sum数组。问题就得到解决。
具体操作可以看看代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n,m,u,v; const int N=5e4+5; vector<int>g[N]; ll cnt[N],sum[N],dep[N]; ll mn; map<int,map<int,int> >qaq; void dfs1(int p,int fa){ dep[p]=dep[fa]+1;//求出每个点的深度也就是到根(1)的距离啦 cnt[p]=1; for (int i=0;i<g[p].size();i++) if (g[p][i]!=fa) {dfs1(g[p][i],p);cnt[p]+=cnt[g[p][i]];} } void dfs2(int p,int fa){ for (int i=0;i<g[p].size();i++) if (g[p][i]!=fa) {sum[g[p][i]]=sum[p]-cnt[g[p][i]]+(n-cnt[g[p][i]]); mn=min(mn,sum[g[p][i]]); dfs2(g[p][i],p);} } int main() { cin>>n>>m; for (int i=1;i<=m;i++){ scanf("%d%d",&u,&v); if (qaq[u][v]==0&&qaq[v][u]==0){//记得判断重边 qaq[u][v]=1; qaq[v][u]=1; g[u].push_back(v); g[v].push_back(u); } } dep[0]=-1; dfs1(1,0); for (int i=1;i<=n;i++) sum[1]+=dep[i];//求出一个sum用于转移 mn=sum[1]; dfs2(1,0); int first=1; for (int i=1;i<=n;i++){ if (sum[i]==mn){ if (first){ printf("%d",i); first=0; } else printf(" %d",i); } } puts(""); return 0; }