bzoj4316小C的独立集(dfs树/仙人掌+DP)
本题有两种写法,dfs树上DP和仙人掌DP。
先考虑dfs树DP。
什么是dfs树?其实是对于一棵仙人掌,dfs后形成生成树,找出非树边(即返祖边),然后dfs后每条返祖边+其所覆盖的链构成了一个环(很显然覆盖的链互不相交),然后可以确定每条边出现在哪个环中,然后可以解决一些简单的仙人掌DP问题,不用写tarjan了。
这道题的第一种方法就是dfs树DP,题目是求仙人掌的最大独立集。
首先树形DP,没有环应该很好求,有环的情况,考虑记录环上的点的top和end(注意环顶部不用记录,因为环顶部可能属于另一个环底)。然后可以直接DP了,相比于树形DP多记录一维环底部是否选,改成开两个数组,g[i][0/1]表示强制让环底部不选,该点不选/选的最大值,f[i][0/1]表示无所谓让环底部选不选,该点不选/选的最大值。首先对点u,初始化:f[u][1]=1,若不为环底部,则g[u][1]=1。dfs到边u->v,g[u][1]+=g[v][0],因为无论如何该点选了则儿子、底部势必不选,如果两点不在一个环上,则g[u][0]+=max(f[u][0],f[u][1]),反之g[u][0]+=max(g[v][0],g[v][1])。然后考虑转移f数组,首先f[u][0]+=max(f[v][0],f[v][1]),因为u不选,一切自由,如果u不为环的顶部,则f[u][1]+=f[v][0],反之,底部也不能选,f[u][1]+=g[v][0]。
贴不贴code也无所谓了,都说了这么多,不过还是贴一个吧。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N=50086; int n,m,dep[N],fa[N],tp[N],ed[N],f[N][2],g[N][2]; vector<int>G[N]; void dfs(int u) { dep[u]=dep[fa[u]]+1; for(int i=0;i<G[u].size();i++)if(!dep[G[u][i]])fa[G[u][i]]=u,dfs(G[u][i]); } void walk(int u,int v){int x=v;while(x!=u)tp[x]=u,ed[x]=v,x=fa[x];} void dp(int u) { f[u][1]=1; if(u!=ed[u])g[u][1]=1; for(int i=0;i<G[u].size();i++) if(dep[u]+1==dep[G[u][i]]) { int v=G[u][i]; dp(v); if(ed[u]!=ed[v])g[u][0]+=max(f[v][0],f[v][1]);else g[u][0]+=max(g[v][0],g[v][1]); g[u][1]+=g[v][0]; if(tp[v]!=u)f[u][1]+=f[v][0];else f[u][1]+=g[v][0]; f[u][0]+=max(f[v][0],f[v][1]); } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x); dfs(1); for(int u=1;u<=n;u++) for(int i=0;i<G[u].size();i++) if(dep[u]+1<dep[G[u][i]])walk(u,G[u][i]); dp(1); printf("%d",max(f[1][0],f[1][1])); }
这题也可以仙人掌DP,当然不需要建立圆方树。tarjan的本质是构建dfs树,然后f[i][0/1]表示当前节点为i,不选/选的最大值,当边是树边时可以直接转移,反之暂时不转移。等到发现其为环的顶部时,然后从底部向顶部推一遍答案。由于是一条链,维护f0和f1表示当前其不选/选的最大值,然后推两遍即可,注意推一遍求f[u][1]时,强制不选底部,即f1=-inf,然后就是裸的树形DP了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N=50086; int n,m,cnt,dfn[N],low[N],fa[N],f[N][2]; vector<int>G[N]; void dp(int u,int v) { int t0,t1,f0=0,f1=0; for(int i=v;i!=u;i=fa[i])t0=f0+f[i][0],t1=f1+f[i][1],f0=max(t0,t1),f1=t0; f[u][0]+=f0; f0=0,f1=-1e9; for(int i=v;i!=u;i=fa[i])t0=f0+f[i][0],t1=f1+f[i][1],f0=max(t0,t1),f1=t0; f[u][1]+=f1; } void tarjan(int u,int pre) { fa[u]=pre,dfn[u]=low[u]=++cnt; f[u][1]=1,f[u][0]=0; for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(!dfn[v])tarjan(v,u),low[u]=min(low[u],low[v]); else if(v!=pre)low[u]=min(low[u],dfn[v]); if(low[v]>dfn[u])f[u][1]+=f[v][0],f[u][0]+=max(f[v][0],f[v][1]); } for(int i=0;i<G[u].size();i++)if(fa[G[u][i]]!=u&&dfn[u]<dfn[G[u][i]])dp(u,G[u][i]); } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x); tarjan(1,0); printf("%d",max(f[1][0],f[1][1])); }