【Codeforces-1325 F】DFS树 求简单环 独立点集
题意
给出一个 n 个顶点,m 条无向边的连通图,现在你可以选择以下两个问题中的一个问题解决。
- 找到一个大小为 \(⌈ \sqrt{n} ⌉\)的独立点集
- 找到一个大小最少为为\(⌈ \sqrt{n} ⌉\) 的简单环
思路
本题需要学习DFS树
在 DFS 的时候如果遇到回边,判断这个环中的点是不是满足条件,满足直接输出。
给出一个结论:如果不存在满足条件的简单环,那么一定存在满足条件的独立点集。
证明:
如果一个点有超过 \(\sqrt{n} - 2\) 条父边(就是从其祖先节点连向这个点的边),那么就肯定有一个最少为 $ \sqrt{n} $的环。
所以现在每个点最多有 \(\sqrt{n} - 2\) 条父边,那么加上它的一条向下的子边,最多会影响 \(\sqrt{n} - 1\) 个点不能被选择,那么就可以选出 \(\sqrt{n}\) 大小的独立集。
代码
#include<bits/stdc++.h>
#define pb push_back
const int N=1e6+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
vector<int>g[N],ans,vec;
int n,m,lim;
int dep[N],vis[N];
void dfs(int u)
{
vec.pb(u);
dep[u]=vec.size();
for(int v:g[u])
{
if(!dep[v])
dfs(v);
else
{
if(dep[u]-dep[v]+1>=lim)//找到环
{
printf("2\n%d\n",dep[u]-dep[v]+1);
for(int i=dep[v]-1;i<dep[u];i++)
printf("%d ",vec[i]);
printf("\n");
exit(0);
}
}
}
if(!vis[u])//vis为1表示这个点被影响过了
{
ans.pb(u);
for(int v:g[u])
vis[v]=1;
}
vec.pop_back();
}
int main()
{
scanf("%d%d",&n,&m);
lim=(int)sqrt(n-1)+1;
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
dfs(1);
printf("1\n");
for(int i=0;i<lim;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
还有一道题与此类似:
题意要求是恰好 \(⌈ \frac{k}{2} ⌉\) 个独立点,或者最多 $ k $ 个点的简单环。
假如不存在满足条件的环,那么环的大小最小为 \(k+1\) ,那么在换上隔一个点取一个就可以得到独立点集。
代码 (与上类似)
#include<bits/stdc++.h>
#define pb push_back
const int N=1e6+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
vector<int>g[N],ans,vec;
int n,m,k;
int dep[N],vis[N];
void dfs(int u,int fa)
{
vec.pb(u);
dep[u]=vec.size();
for(int v:g[u])
{
if(v==fa) continue;
if(!dep[v])
dfs(v,u);
else
{
if(dep[u]-dep[v]+1>2&&dep[u]-dep[v]+1<=k)
{
printf("2\n%d\n",dep[u]-dep[v]+1);
for(int i=dep[v]-1;i<dep[u];i++)
printf("%d ",vec[i]);
printf("\n");
exit(0);
}
}
}
if(!vis[u])
{
ans.pb(u);
for(int v:g[u])
vis[v]=1;
}
vec.pop_back();
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
dfs(1,1);
printf("1\n");
for(int i=0;i<(k+1)/2;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
/*
*/