題意:
有n個人要參加聚會,1~n分別表示他們的序列,m行表示他們之間的關係,這n個人一次進場,若進場後發現沒有自己的朋友,這個人就會不開心。現在要求排他們的入場順序,使得不開心的人數最少,同時,進場人的字典序最小。
思路:
由於他們的關係可以用聯通圖表示,則每一個聯通塊必然至少會有一個人不開心,所以用並查集判斷聯通塊個數。同時再用0號表示超級源點,依次將他們的朋友放進優先隊列中,出隊順序就是入場順序。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000050;
int pre[MAXN],cnt; //保存節點的直接父節點
vector<int>ve[MAXN];
int ans[MAXN],vis[MAXN];
//查找x的根節點
int Find(int x){
if(pre[x]!=x)
pre[x]=Find(pre[x]);//路徑壓縮,本結點更新爲根結點的子結點
return pre[x];
}
//連接兩個連通塊
void join(int x,int y) {
int fx=Find(x),fy=Find(y);
if(fx>fy) pre[fx]=fy;
else pre[fy]=fx;
}
void BFS(int x)
{
priority_queue<int,vector<int>,greater<int> > q;
q.push(x);
while(!q.empty()){
int now = q.top();q.pop();
if(!vis[now]){
vis[now]=1;
ans[cnt++]=now; //出隊順序是入場順序
for(int i=0;i<ve[now].size();i++){
if(!vis[ve[now][i]]){
q.push(ve[now][i]);
}
}
}
}
}
int main() {
int T;
cin>>T;
while(T--){
int N,M,a,b,i,j,ans1=0;
scanf("%d%d",&N,&M);
//初始化pre數組
for(i=0;i<=N;i++){
pre[i]=i;//根據連通情況,構建pre數組
vis[i]=0;
ve[i].clear();
}
for(i=1;i<=M;i++) {
scanf("%d%d",&a,&b);
ve[a].push_back(b);
ve[b].push_back(a);
join(a,b);
}
for(i=1;i<=N;i++){
if(pre[i]==i){
ans1++; //計算連通子圖的個數ans
ve[0].push_back(i);
}
}
cnt=0;
BFS(0);
cout<<ans1<<endl;
for(int i=1;i<cnt;i++){
if(i==1) printf("%d",ans[i]);
else printf(" %d",ans[i]);
}printf("\n");
}
return 0;
}