题目:4316: 小C的独立集
图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。求最大独立子集。(算是个特殊的仙人掌)
dp表示的情况都是i节点作为i祖先时间戳环中一点的情况
环末节点: 在这个环中的下一个节点刚刚碰到已访问时间戳的节点
dp[i][0], dp[i][1]: i号节点的环末节点可能存在情况
0表示这一节点不存在时,从环末节点到当前i节点的最大独立集的size
1表示这一节点存在时,从环末节点到当前i节点的最大独立集的size
dp[i][2], dp[i][3]: i号节点的环末节点强制不存在情况
2表示这一节点不存在时,从环末节点到当前i节点的最大独立集的size
3表示这一节点存在时,从环末节点到当前i节点的最大独立集的size
在实现的过程注意以下细节:
- 当i节点为环末节点时分为度为2和不为2的:
- 度为2和不为2都初始化dp[i][1] = 1其他为0
- 度不为2时dp[i][3]强制不存在时可以无条件接受衍生环的贡献即+=max(dp[v][0], dp[v][1])
- dp[i][2]无条件接受衍生环的贡献
- 到达一个点时先跑一遍初度,确定这一点是不是环末点,如果是,立即对时间戳染色
code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 3;
const int maxm = 6e4 + 3;
int n, m, x, y, top=0, cnt=0, t, col;
int f[maxn];
int dfn[maxn];
int low[maxn];
int dp[maxn][4];
struct edge
{
int u;
int v;
int next;
}e[maxm * 2];
void Add(int u, int v)
{
++top;
e[top].u=u;
e[top].v=v;
e[top].next=f[u];
f[u]=top;
}
int read()
{
int x=0;
int k=1;
char c=getchar();
while(c>'9'||c<'0')
{
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9')
x=x*10+c-'0',
c=getchar();
return x*k;
}
int tarjan(int now, int fa)
{
dfn[now]=low[now]=++cnt;
dp[now][1] = dp[now][3] = 1;
int final = 0;
for(int i=f[now];i!=-1;i=e[i].next) if (e[i].v != fa)
{
int v = e[i].v;
if (dfn[v])
{
dp[now][3]--;
low[now]=min(low[now],dfn[v]);
final = 1;
}
}
for(int i=f[now];i!=-1;i=e[i].next) if (e[i].v != fa)
{
int v = e[i].v;
if(!dfn[v]) // 不作为环末节点时
{
tarjan(v, now);
if (low[v] < low[now]) // 从now祖先的时间戳衍生的环回溯回来
{
dp[now][0] += max(dp[v][1], dp[v][0]);
dp[now][1] += dp[v][0];
dp[now][2] += max(dp[v][2], dp[v][3]);
dp[now][3] += dp[v][2];
}
else // 从now时间戳衍生的环回溯回来
{
dp[now][0] += max(dp[v][1], dp[v][0]);
dp[now][1] += dp[v][2]; // 当now存在时自己在衍生环中相邻位置不允许有节点存在
dp[now][2] += max(dp[v][1], dp[v][0]);
if (final) // 在环末,强制不存在就是now自己时,now在衍生环相邻位置没特殊要求
dp[now][3] += max(dp[v][1], dp[v][0]);
else
dp[now][3] += dp[v][2];
}
low[now]=min(low[now],low[v]);
}
}
return max(dp[now][0], max(dp[now][2], dp[now][3]));
}
int main()
{
/*
freopen("data.in", "r", stdin); //从文件data.in中读入数据
freopen("ZJ.out", "w", stdout); //输出的结果存在ZhengJie.out文件中
/**/
n=read();
m=read();
memset(f,-1,sizeof f);
for(int i=1;i<=m;++i)
{
x=read();
y=read();
Add(x, y);
Add(y, x);
}
printf("%d\n", tarjan(1, 0));
return 0;
}
/*
freopen("data.in", "r", stdin); //从文件data.in中读入数据
freopen("ZJ.out", "w", stdout); //输出的结果存在ZhengJie.out文件中
/**/
/*
11 13
1 2
2 3
3 4
4 5
5 6
6 1
2 7
7 8
8 9
9 2
2 10
10 11
11 2
*/