题解 P1330 【封锁阳光大学】
安利一波自己的博客
看到题解中各位大佬都是用的黑白染色,蒟蒻表示不才,于是写了一发树形dp过了此题。
思路:
首先,我们如果把这个图当做树来看的话,那么它有可能不止一棵树,所以它有可能是森林。我们可以用并查集来维护每一个连通块,设 \(root[i]\) 表示连通块 \(i\) 的根节点是哪一个,注意,最开始没有连边的时候,每一个连通块的根都是他自己。
解决完了遍历的问题后,我们设 \(f[i][0/1]\) 表示在以i为根的子树上选/不选这个点所需的最少的河蟹数量,则得出转移方程:
\(f[i][0]=\sum_{v\in son[i]}f[v][1]\)
\(f[i][1]=\sum_{v\in son[i]}f[v][0] +1\)
然后我们将每一个联通块跑一遍树形 \(dp\),再将答案累加每一个根放还是不放的最大值,最后输出答案即可。
最后需要注意的一个点,如果 \(m\geq n\) 或者遍历的时候遇到了已经访问过的点,那么说明这张图有回路,不可能存在合法答案,输出 \(Impossible\)
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
struct Node
{
int t;
int next;
}node[200011];
bool book[10011];
int root[10011],f[10011];
int head[10011],tot=0,dp[10011][2];
int n,m;
void init()
{
for(int i=1;i<=n;++i)
f[i]=i,root[i]=i;
}
int getf(int v)
{
if(f[v]==v) return v;
else return f[v]=getf(f[v]);
}
void merge(int a,int b)
{
int t1=getf(a),t2=getf(b);
if(t1!=t2) f[t2]=t1,root[t2]=root[t1];
}
void add(int x,int y)
{
node[++tot].t=y;
node[tot].next=head[x];
head[x]=tot;
}
void dfs(int f,int fa)
{
if(book[f])
{
cout<<"Impossible";
exit(0);
}
book[f]=1;
dp[f][1]=1;
bool flag=0;
for(int i=head[f];i;i=node[i].next)
{
int u=node[i].t;
if(u==fa) continue;
dfs(u,f);
dp[f][0]+=dp[u][1];
dp[f][1]+=dp[u][0];
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int ans=0;
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d %d",&x,&y);
merge(x,y);
add(x,y);
add(y,x);
}
if(m>=n)
cout<<"Impossible";
else
{
for(int i=1;i<=n;++i)
if(!book[i])
{
int ta=getf(i);
dfs(root[ta],0);
ans+=min(dp[ta][0],dp[ta][1]);
}
printf("%d",ans);
}
return 0;
}