zoukankan      html  css  js  c++  java
  • [BZOJ4316]小C的独立集(仙人掌+树形DP)

    [BZOJ4316]小C的独立集(仙人掌+树形DP)

    题面

    图论王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨。
    这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多。
    小D虽然图论很弱,但是也知道无向图最大独立集是npc,但是小C很仁慈的给了一个很有特点的图: 图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。小C说这样就会比较水了。
    小D觉得这个题目很有趣,就交给你了,相信你一定可以解出来的。

    分析

    考虑对仙人掌Tarjan(但是不必要建圆方树),桥边之间可以直接按树上最大独立集转移(设(dp_{x,0/1})表示(x)子树不选/选)。每次出现了环(即DFS的时候搜到边((x,y)),出现(dfn_x<dfn_y)),就沿着父亲把树上的链提出来,就得到了环上的节点。

    再考虑环上如何转移。设底端的点为(y),顶端的点为(x),讨论这两个点的选择情况。若(x)不选,则(y)选不选都可以,直接按树的情况转移即可。若(x)选,则(y)必须不选,此时要把(dp_{y,1})设为(-infin),然后再跑DP转移。

    void calc(int x,int y){//x到y的非树边构成一个环 
    	int f0=0,f1=0;//环上下一个点不选/选的转移 
    	//此时x不选 
    	for(int i=y;i!=x;i=fa[i]){
    		int t0=f0+dp[i][0];
    		int t1=f1+dp[i][1];
    		f0=max(t0,t1);//下一个点不选,所以i选不选都没关系
    		f1=t0;//下一个点选 
    	}
    	dp[x][0]+=f0;
    	//此时x选
    	f0=0,f1=-INF;//-INF保证y一定不选,因为(x,y)有边
    	for(int i=y;i!=x;i=fa[i]){
    		int t0=f0+dp[i][0];
    		int t1=f1+dp[i][1];
    		f0=max(t0,t1);//下一个点不选,所以i选不选都没关系
    		f1=t0;//下一个点选 
    	}
    	dp[x][1]+=f1;
    }
    

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #define maxn 50000
    #define maxm 60000
    #define INF 0x3f3f3f3f
    using namespace std;
    struct edge{
    	int from;
    	int to;
    	int next;
    }E[maxm*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v){
    	esz++;
    	E[esz].from=u;
    	E[esz].to=v;
    	E[esz].next=head[u];
    	head[u]=esz;
    } 
    int fa[maxn+5];
    int dp[maxn+5][2];
    void calc(int x,int y){//x到y的非树边构成一个环 
    	int f0=0,f1=0;//环上下一个点不选/选的转移 
    	//此时x不选 
    	for(int i=y;i!=x;i=fa[i]){
    		int t0=f0+dp[i][0];
    		int t1=f1+dp[i][1];
    		f0=max(t0,t1);//下一个点不选,所以i选不选都没关系
    		f1=t0;//下一个点选 
    	}
    	dp[x][0]+=f0;
    	//此时x选
    	f0=0,f1=-INF;//-INF保证y一定不选,因为(x,y)有边
    	for(int i=y;i!=x;i=fa[i]){
    		int t0=f0+dp[i][0];
    		int t1=f1+dp[i][1];
    		f0=max(t0,t1);//下一个点不选,所以i选不选都没关系
    		f1=t0;//下一个点选 
    	}
    	dp[x][1]+=f1;
    }
    int tim,dfn[maxn+5],low[maxn+5];
    void tarjan(int x,int last_edge){
    	dfn[x]=low[x]=++tim;
    	dp[x][0]=0;dp[x][1]=1;
    	for(int i=head[x];i;i=E[i].next){
    		int y=E[i].to;
    		if(!dfn[y]){
    			fa[y]=x;
    			tarjan(y,i);
    			low[x]=min(low[x],low[y]);
    		}else if(i!=(last_edge^1)) low[x]=min(low[x],dfn[y]);
    		if(dfn[x]<low[y]){//桥边直接转移 
    			dp[x][0]+=max(dp[y][0],dp[y][1]);
    			dp[x][1]+=dp[y][0];
    		}
    	}
    	for(int i=head[x];i;i=E[i].next){
    		int y=E[i].to;
    		if(x!=fa[y]&&dfn[x]<dfn[y]) calc(x,y);//出现了环 
    	}
    }
    
    int n,m;
    int main(){
    	int u,v;
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d %d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	tarjan(1,0);
    	printf("%d
    ",max(dp[1][0],dp[1][1]));
    }
    
  • 相关阅读:
    VS远程调试亲历
    IIS7.5 配置虚拟目录的经历
    IE 兼容一问题一小记
    寻找 IBatisNet 批量插入(批量复制) 的心路历程
    Linq 多连接及 left join 实例 记录
    easyui treegrid idField 所在属性中值有花括号(如Guid)当有鼠标事件时会报错,行记录一下
    HDU1754
    HDU1166
    线段树模板
    HDU1599(Floyd最小环)
  • 原文地址:https://www.cnblogs.com/birchtree/p/14068871.html
Copyright © 2011-2022 走看看