BZOJ 4316: 小C的独立集 解题报告

4316: 小C的独立集

Description

图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨。

这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多。

小D虽然图论很弱,但是也知道无向图最大独立集是npc,但是小C很仁慈的给了一个很有特点的图: 图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。小C说这样就会比较水了。

小D觉得这个题目很有趣,就交给你了,相信你一定可以解出来的。

Input

第一行,两个数(n)(m),表示图的点数和边数。

(2sim m+1)行,每行两个数(x)(y),表示(x)(y)之间有一条无向边。

Output

输出这个图的最大独立集。

HINT

(100\%) (nle 50000,mle 60000)


不用建圆方树的仙人掌。

最大独立集:选出最大的点集,使得两两没有边连接。

对普通树边按普通的(dp)即可

对环上的点,把环的头拽出来,然后从它屁股dp上去就行了


Code:

#include <cstdio>
const int N=6e4+10;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
const int inf=0x3f3f3f3f;
int n,m,dp[N][2],dfn[N],low[N],dfsclock,fa[N];
void cal(int rt,int now)
{
    int t=now,s0=0,s1=0;
    while(now!=rt)
    {
        int tmp=s0;
        s0=max(s0,s1)+dp[now][0];
        s1=tmp+max(dp[now][1],dp[now][0]);
        now=fa[now];
    }
    dp[rt][0]+=max(s0,s1);
    s0=-inf,s1=0,now=t;
    while(now!=rt)
    {
        int tmp=s0;
        s0=max(s0,s1)+dp[now][0];
        s1=tmp+max(dp[now][1],dp[now][0]);
        now=fa[now];
    }
    dp[rt][1]+=s0;
}
void dfs(int now)
{
    dp[now][1]=1;
    dfn[now]=low[now]=++dfsclock;
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=fa[now])
        {
            if(!dfn[v]) fa[v]=now,dfs(v),low[now]=min(low[now],low[v]);
            else low[now]=min(low[now],dfn[v]);
            if(dfn[now]<low[v])//正常树边
                dp[now][0]+=max(dp[v][0],dp[v][1]),dp[now][1]+=dp[v][0];
        }
    for(int v,i=head[now];i;i=Next[i])
        if(fa[v=to[i]]!=now&&dfn[v]>dfn[now])
            cal(now,v);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int u,v,i=1;i<=m;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u);
    dfs(1);
    printf("%d
",max(dp[1][0],dp[1][1]));
    return 0;
}

2018.12.24

原文地址:https://www.cnblogs.com/butterflydew/p/10171246.html