题意 : 现在给你一个图 一个起点,你要求出该点到原图的补图上所有点的最短距离。
思路: 一开始把点分为1 组(与S没有边 的点, 既 一步能够走到的点 )和2组(和S有边的点 既从 S一步走不到的点) 两组点,然后很想当然的认为 只需要考虑1组对2组的影响,如果二组中有一个点使得 并不是1中所有的点都与他有边 ,那么这个点一定是在2步到达,然后考虑2组对 2组的影响,只要2组中有一个点使得 并不是其他所有的二组点都与他有边就为3 ,其他为inf。当然是错的,因为距离S为3 的点还必须确定前一个点是距离为2 的点,并且有可能出现距离为4 ,5,。。。。等等。所以GG
就比如这个样例:
1
5 6
1 3
1 4
1 5
2 4
2 5
3 5
1
当然做法就是两个set 维护,然后bfs就可以。每个点只可能出队列入队一次。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =200005;
const int inf =0x3f3f3f3f;
int n,m,S;
vector< int >ve[N];
set<int >se1;
set<int >se2;
int Ans[N];
set<int >::iterator it;
void init()
{
memset(Ans,inf ,sizeof(Ans));
for(int i=0;i<=n;i++) ve[i].clear();
se1.clear(); se2.clear();
}
void bfs()
{
queue< int >q;
q.push(S);
int u,v;
Ans[S]=0;
while(!q.empty())
{
u=q.front();
q.pop();
for(int i=0;i<ve[u].size();i++){
v=ve[u][i];
if(!se1.count(v)) continue;
se2.insert(v);
se1.erase(v);
}
for( it=se1.begin();it!=se1.end();it++)
{
v=*it;
if(Ans[v]>Ans[u]+1) Ans[v]=Ans[u]+1;
q.push(v);
}
swap(se1,se2);
se2.clear();
}
int cnt=0;
for(int i=1;i<=n;i++){
if(i==S) continue;
if(Ans[i]==inf) printf("-1");
else printf("%d",Ans[i]);
++cnt;
if(cnt==n-1) printf("\n");
else printf(" ");
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
int u,v;
init();
for(int i=1;i<=m;i++){
scanf("%d %d",&u,&v);
ve[u].push_back(v);
ve[v].push_back(u);
}
scanf("%d",&S);
for(int i=1;i<=n;i++){
if(i==S) continue;
se1.insert(i);
}
bfs();
}
return 0;
}
/*
1
5 6
1 3
1 4
1 5
2 4
2 5
3 5
1
*/