題目大意
輸入一個由個頂點和條邊組成的無向圖,找一個長度的環或者是頂點數爲的獨立集(獨立集中任意兩個點沒有直接邊)。
分析過程
此題用樹求解,所謂樹就是將圖進行遍歷之後導出的樹,如下圖所示。(粗線爲樹邊,淺線爲非樹邊)
樹有一個重要的性質,不在同一顆子樹上的兩點之間沒有邊(非樹邊只有可能是在同一條樹鏈上),這個用反證法的思路和的性質能夠容易看出。
樹找出的環不一定是最大環也不一定是最小環。比如下面這組數據。(找最小環需要用)
- 這組數據的最大環是124563最外面這個環,樹中找不到這個環。
- 回到這道題目上面來,在樹上遇到環時判斷對應非樹邊的兩個端點差是否成立,如果成立則直接輸出這個環。(的過程中開堆棧存一下同條樹鏈上已經訪問的歷史節點)
- 否則,樹同一條樹鏈上的高度差爲的頂點之間必然沒有環,因此可以用的方式劃分等價類,所有同餘的頂點兩兩之間必然沒有邊,即可以組成獨立集。由鴿巢原理,一共有個頂點,劃分成個等價類,必然至少有一個等價類中的元素個數d。
這道題也是DFS樹留個坑。
AC代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
typedef long long ll;
int n, m, d, flag, dep[maxn];
vector<int> G[maxn];
stack<int> s;
void dfs(int cur, int par){
dep[cur] = dep[par] + 1;
s.push(cur);
for(auto temp:G[cur]){
if(temp == par) continue;
if(!dep[temp]){
dfs(temp, cur);
if(flag) return;
s.pop();
}else{
if(dep[cur] - dep[temp] >= d - 1){ //找到符合條件的環
cout<<2<<'\n';
flag = 1;
cout<<dep[cur] - dep[temp] + 1<<'\n'<<s.top();
s.pop();
while(!s.empty()){
cout<<' '<<s.top();
if(s.top() == temp) return;
s.pop();
}
}
}
}
}
void solve(){
int cnt[maxn];
memset(cnt, 0, sizeof(cnt));
d = sqrt(n + 0.5);
if(n > d * d) ++d;
dfs(1, 0);
if(!flag){ //存在獨立集
cout<<1<<'\n';
int maxi = 0, p, c = 0;
for(int i=1;i<=n;++i){
cnt[dep[i]%(d-1)]++;
if(cnt[dep[i]%(d-1)] > maxi){
maxi = cnt[dep[i]%(d-1)];
p = dep[i]%(d-1);
}
}
for(int i=1;i<=n;++i){
if(dep[i] % (d - 1) == p){
if(c) cout<<' ';
cout<<i;
++c;
if(c >= d) break;
}
}
}
}
int main(){
int t, i, j, u, v;
ios::sync_with_stdio(false);
cin>>n>>m;
for(i=1;i<=m;++i){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
solve();
return 0;
}