Bzoj_4316 小C的独立集
题意
给定一个仙人掌,求出这个仙人掌的最大独立点集。
\(n \leqslant 5e4,m \leqslant 6e4\)
题解
哈哈,第一次写仙人掌DP,大脑爆炸。
这里用一种直接DP的方式。设\(f_{x,i,j}\)表示点x的选择情况为i,点x到父亲的那条边所在的环中,深度最大(位于底部)的点的选择情况为j的最大方案。转移嘛,自己想想吧。详见代码。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e4;
int n,m,tot,ans;
int pre[maxn*4+8],now[maxn+8],son[maxn*4+8];
int fa[maxn+8],color[maxn+8],f[maxn+8][2][2],dep[maxn+8];
int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
void add(int u,int v)
{
pre[++tot]=now[u];
now[u]=tot;
son[tot]=v;
}
void dfs(int x)
{
dep[x]=dep[fa[x]]+1;
for (int p=now[x];p;p=pre[p])
{
int child=son[p];
if (child==fa[x]) continue;
if (!dep[child])
{
fa[child]=x,dfs(child);
for (int i=0;i<2;i++)
for (int j=0;j<2;j++)
{
int res=0;
for (int k=0;k<2;k++)
for (int l=0;l<2;l++)
{
if (i&k) continue;
if (color[child]!=2&&j!=l) continue;
if (color[child]==1&&k!=l) continue;
if (color[child]==2&&(i&l)) continue;
res=max(res,f[child][k][l]);
}
f[x][i][j]+=res;
}
}
else
if (dep[x]>dep[child])
{
color[x]=1;
int res=x;
while(fa[res]!=child) res=fa[res];
color[res]=2;
}
}
f[x][1][0]++,f[x][1][1]++;
if (color[x]==1)
{
f[x][0][1]=f[x][0][0]=max(f[x][0][1],f[x][0][0]);
f[x][1][1]=f[x][1][0]=max(f[x][1][0],f[x][1][1]);
f[x][1][0]=f[x][0][1]=0;
}
}
int main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
for (int i=1;i<=n;i++)
if (!dep[i])
{
dfs(i);
int res=0;
for (int j=0;j<2;j++)
for (int k=0;k<2;k++)
res=max(res,f[i][j][k]);
ans+=res;
}
printf("%d\n",ans);
return 0;
}