• 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
  • 相关阅读:
    IOS--UILabel的使用方法详细
    一个人不成熟的六大特征:
    UIView
    objective-c 错题
    洛谷P1039 侦探推理(模拟)
    洛谷P1038 神经网络(bfs,模拟,拓扑)
    FBI树-数据结构(二叉树)
    二叉树遍历(flist)(二叉树,已知中序层序,求先序)
    求先序排列(二叉树已知中序和后序,求先序)
    哈理工2015暑假集训 zoj 2975 Kinds of Fuwas
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/11154233.html
Copyright © 2020-2023  润新知