BZOJ4316,解法一(tarjan过程中dp)

题目: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

在实现的过程注意以下细节:

  1. 当i节点为环末节点时分为度为2和不为2的:
    1. 度为2和不为2都初始化dp[i][1] = 1其他为0
    2. 度不为2时dp[i][3]强制不存在时可以无条件接受衍生环的贡献即+=max(dp[v][0], dp[v][1])
  2. dp[i][2]无条件接受衍生环的贡献
  3. 到达一个点时先跑一遍初度,确定这一点是不是环末点,如果是,立即对时间戳染色

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
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值