题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=6011
题意
有n个人m个关系,每一对关系代表这两人认识,关系不具有传递性,现在你要安排这n个人进入一个舞会,如果第i个人进入了舞会大厅但是发现没有一个认识的人,那就会不高兴。问你怎么安排顺序这n个人进入舞会大厅,使得不开心的人数最少。如果有多种方案,输出字典序最小的方案。
解题思路
首先每一个连通块肯定有一个人是不开心的,并且可以做到只有一个人是不开心的,所以有多少连通块,就有多少人不开心。对于字典序,只需要搞个优先队列遍历就可以了。然后因为有多个连通块,肯定不能一个块一个块的遍历,所以需要弄一个超级源点连到所有的起点上,然后再遍历,注意连到每个联通块的起点,这个起点需要是整个块的最小值,这样肯定使得结果最优。而让块的祖先节点是最小值,这一点可以用并查集稍作修改实现。
(以上文字来源:https://blog.csdn.net/qq_41289920/article/details/89605547 )
注意:可能会出现 (1,2) (2,1) 这种情况,那么1的朋友(vector存)里面会有两个2,优先队列bfs遍历的时候要避免重复遍历2的朋友们,否则在报wa之前会内存超限!!(终于找到原因了(;´༎ຶД༎ຶ`))
ac代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define maxn 1000005
using namespace std;
int f[maxn],n,m,x,y;
int ans[maxn],num,vis[maxn];
vector<int> fr[maxn];
int findf(int x) {
return f[x] == x ? x : f[x] = findf(f[x]);
}
void bfs(int x) {
priority_queue<int,vector<int>,greater<int> > q;
q.push(x);
while(!q.empty()) {
int k = q.top();q.pop();
if(vis[k] == 1) continue;
vis[k] = 1;
ans[num++] = k;
int up = fr[k].size();
for(int i = 0; i<up; i++) {
int v = fr[k][i];
if(!vis[v])
q.push(v);
}
}
}
int main()
{
int t;
cin>>t;
while(t--) {
num=0;
scanf("%d%d",&n,&m);
for(int i = 0; i<=n; i++) f[i] = i,fr[i].clear(),vis[i]=0;
for(int i = 1; i<=m; i++) {
scanf("%d%d",&x,&y);
fr[x].push_back(y);fr[y].push_back(x);
int fx = findf(x);
int fy = findf(y);
if(fx>fy) f[fx] = fy;
else f[fy]=fx;
}
int ans1 = 0;
for(int i = 1; i<=n; i++) {
if(f[i] == i) fr[0].push_back(i),ans1++;
}
bfs(0);
printf("%d\n",ans1);
for(int i = 1; i<num; i++) {
if(i!=1) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
return 0 ;
}