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也无所谓了,都说了这么多,不过还是贴一个吧。

#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]));
}
View Code

这题也可以仙人掌DP,当然不需要建立圆方树。tarjan的本质是构建dfs树,然后f[i][0/1]表示当前节点为i,不选/选的最大值,当边是树边时可以直接转移,反之暂时不转移。等到发现其为环的顶部时,然后从底部向顶部推一遍答案。由于是一条链,维护f0和f1表示当前其不选/选的最大值,然后推两遍即可,注意推一遍求f[u][1]时,强制不选底部,即f1=-inf,然后就是裸的树形DP了

#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]));
}
View Code

 

posted @ 2019-07-08 22:17  hfctf0210  阅读(283)  评论(0编辑  收藏  举报