ZOJ - 4109 - Welcome Party (并查集 + BFS + 优先队列)
题目大意:n个人,m种关系 (a和b是朋友),可以看作 n个点,m条边, 用图论的知识解题
问在使最少人不开心的情况下,输出进房间字典序排序最小的顺序。(如果在小A进房间之前房间内没有他的朋友,他就不开心)
使用并查集分块,每个并查集的根节点和独立点(无朋友)的总个数就是输出的不开心的人数。
使用BFS和优先队列遍历存入的图,保证字典序最小。将路径存入答案数组。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6+5; int s[maxn]; int vis[maxn]; vector<int> Edge[maxn]; //二维! vector<int> sto; //答案 int Find(int t) { return t==s[t]?t:s[t]=Find(s[t]); } void Merge(int st,int ed) { int t1=Find(st); int t2=Find(ed); if(t1==t2) return; if(t1>t2)swap(t1,t2); s[t2]=t1; } void bfs(int root) { priority_queue<int,vector<int>,greater<int> > Q; while(!Q.empty()) Q.pop(); Q.push(root); vis[root] = 1; while(!Q.empty()) { int t = Q.top(); Q.pop(); sto.push_back(t); // cout << " size = " << sto.size() << endl; for(int i = 0; i < Edge[t].size(); i++) { int temp = Edge[t][i]; if(vis[temp]) continue; vis[temp] = 1; Q.push(temp); } } } int main() { int T, n, m; cin >> T; while(T--) { int ans = 0; cin >> n >> m; sto.clear(); //清空答案数组!! for(int i = 0; i <= n; i++) { //从0开始,把所有根节点和无朋友的点存在Edge[0]里面 s[i] = i; //并查集数组 Edge[i].clear(); //存边(两点 vis[i] = 0; //标记数组 } for(int i = 1; i <= m; i++) { int x, y; cin >> x >> y; Edge[x].push_back(y); //关系是相互的 所以在Merge时需要判断大小 Edge[y].push_back(x); Merge(x, y); } for(int i = 1; i <= n; i++) { if(s[i] == i) { //所有的根节点 和 不存在边的点(无朋友的) ans ++; Edge[0].push_back(i); } } bfs(0); printf("%d\n",ans); int len = sto.size(); for(int i = 1; i < len-1; i++) { cout << sto[i] << " "; } cout << sto[len-1] << endl; } return 0; }