zoukankan      html  css  js  c++  java
  • 树形dp初步

    其实很早之前就学过树形dp,今天总接一下。树形dp就是一个在树上跑的dp(滑稽)

    先是一道板子题:树上最大独立集

    直接上代码了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    struct node
    {
        int x,y,next;
    };
    node a[110005];
    int len,last[110005];
    void add(int x,int y)
    {
        a[++len].x = x;
        a[len].y = y;
        a[len].next = last[x];
        last[x] = len;
    }
    int fa[11000],son[11000];
    int f[11000][2];
    int v[110000];
    /*
    f[i][1]代表请i的最大值
    f[i][0]代表不请i的最大值 
    */ 
    template <class T>
    void read(T &x)
    {
        char c;
        int op = 0;
        while(c = getchar(),c > '9' || c < '0')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(),c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op == 1)
            x = -x;
    }
    void treedp(int x)
    {
        f[x][1] = v[x];
        for(int k = last[x];k;k = a[k].next) //相当于dfs 
            treedp(a[k].y);
        for(int k = last[x];k;k = a[k].next)
        {
            int y = a[k].y;
            f[x][1] += f[y][0]; //dp转移式① 
        }
        f[x][0] = 0;
        for(int k = last[x];k;k = a[k].next)
        {
            int y = a[k].y;
            f[x][0] += max(f[y][0],f[y][1]);//dp转移式② 
        }
    }
    int main()
    {
        int n;
        read(n);
        memset(f,-1,sizeof(f));
        memset(fa,0,sizeof(fa));
        for(int i = 1;i <= n;i++)
            read(v[i]);
        int xx,yy;len = 0;
        memset(last,0,sizeof(last));
        while(scanf("%d%d",&xx,&yy) != EOF)
        {
            if(xx == 0 && yy == 0)
            {
                break;
            }
            add(yy,xx);
            fa[xx] = yy; //找根节点 
        }
        int root = 0;
        for(int i = 1;i <= n;i++)
            if(fa[i] == 0)
            {
                root = i;
                break;
            }
        treedp(root);
        printf("%d
    ",max(f[root][0],f[root][1]));
        return 0;
    }

    然后还有几个稍微比这个难一点的题,比如:加分二叉树

    【问题描述】
    设一个有n个节点的二叉树的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。
    每个节点都有一个分数(均为正整数),记第i个节点的分数为di, 每棵子树都有一个加分,任一棵子树subtree的加分计算方法如下:
        subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
        若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
        试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
        (1)tree的最高加分
        (2)tree的前序遍历
    【输入格式】
        第1行:一个整数n(n<30),为节点个数。
        第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
    【输出格式】
        第1行:一个整数,为最高加分(结果不会超过2,0 0000 0000)。
        第2行:n个用空格隔开的整数,为该树的前序遍历。
    【样例输入】
    5
    5 7 1 2 10
    【样例输出】
    145
    3 1 2 4 5

    这个题需要枚举中间的断点,然后进行dp。dp比之前简单了,但是其他的要难一些。

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int root[50][50],d[50],f[50][50];
    void pre_visit(int l,int r)
    {
        if(l <= r)
        {
            cout<<root[l][r]<<" ";
            pre_visit(l,root[l][r] - 1);
            pre_visit(root[l][r] + 1, r);
        }
    }
    int main()
    {
        int m;
        cin>>m;
        for(int i = 0;i <= m;i++)
        {
            for(int j = 0;j <= m;j++)
            {
                f[i][j] = 1;
            }
        }
        for(int i = 1;i <= m;i++)
        {
            cin>>d[i];
            root[i][i] = i;
            f[i][i] = d[i];
        }
        for(int k = 2;k <= m;k++)
        {
            for(int l = 1;l <= m - k + 1;l++)
            {
                int r = l + k - 1;
                for(int i = l;i <= r;i++)
                {
                    if(f[l][r] < f[l][i - 1] * f[i + 1][r] + d[i])
                    {
                        root[l][r] = i;
                        f[l][r] = f[l][i - 1] * f[i + 1][r] + d[i];
                    }
                }
            }
        }
        cout<<f[1][m]<<endl;
        cout<<root[1][m]<<" ";
        pre_visit(1,root[1][m] - 1);
        pre_visit(root[1][m] + 1,m);
    }

    还有一个皇宫看守,和最大独立点集很像

    【问题描述】
    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。 皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;有边直接相连的宫殿可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。 可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
     
    编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
    【输入格式】 
    输入文件中数据表示一棵树,描述如下: 
    第1行 n,表示树中结点的数目。 
    第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<I<=N),在该宫殿安置侍卫所需的经费K,该点的儿子数M,接下来M个数,分别是这个节点的M个儿子的标号R1,R2,...,RM。对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。 
    【输出格式】
    输出文件仅包含一个数,为所求的最少的经费。 
    输入样例:
    6
    1 30 3 2 3 4
    2 16 2 5 6
    3 5 0
    4 4 0
    5 11 0
    6 5 0
    输出样例:
    25

    这个题很好想。

    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    struct node
    {
        ll x,y,next;
    };
    node a[110000];
    ll v[100000],f[100000][5];
    ll last[200000],len = 0;
    bool bk[50000];
    /*
    i节点安全代表i节点和子树安全
    不安全代表i节点不安全,但是子树安全 
    f[i][0]表示x点不放人,但是安全
    f[i][1]表示x点不放人,不安全
    f[i][2]表示x点放人,所以安全
    f[i][3]表示x点放人,但是不安全,显然不成立 
    */
    void treedp(int x)
    {
        f[x][2] = v[x];
        f[x][1] = 0;f[x][0] = 0;
        ll minn = 2147364847;
        bool bkk = false;
        for(int k = last[x];k;k = a[k].next)
        {
            ll y = a[k].y;
            if(bk[y] == false)
            {
                bk[y] = true;
                treedp(y);
                minn = min(f[y][2] - f[y][0],minn);
                f[x][0] += min(f[y][0],f[y][2]);
                if(f[y][2] <= f[y][0])
                {
                    bkk = true;
                }//一定选f[i][2] 
                f[x][1] += f[y][0];
                f[x][2] += min(f[y][2],min(f[y][1],f[y][0]));
            }
        }
        if(bkk == false)
        {
            f[x][0] += minn;
        }
    }
    void add(int x,int y)
    {
        a[++len].x = x;
        a[len].y = y;
        a[len].next = last[x];
        last[x] = len;
    }
    int main()
    {
        ll n;
        cin>>n;
        memset(f,0,sizeof(f));
        for(int i = 1;i <= n;i++)
        {
            ll x,m,k;
            cin>>x>>k>>m;
            v[x] = k;
            for(int j = 1;j <= m;j++)
            {
                ll y;
                cin>>y;
                add(x,y);
                add(y,x);
            }
        }
        ll root = 1;
        memset(bk,false,sizeof(bk));
        bk[root] = true;
        treedp(root);
        cout<<min(f[root][0],f[root][2]);
        return 0;
    }
  • 相关阅读:
    ConcurrentHashMap的使用和原理
    记录下项目中常用到的JavaScript/JQuery代码一(大量实例)
    layer ui插件显示tips时,修改字体颜色
    flash上传文件,如何解决跨域问题
    ubuntu下的mv命令
    Semantic 3D
    shellnet运行train_val_seg.py
    Tensorflow的不足之处
    用pip命令把python包安装到指定目录
    ubuntu建立文件或者文件夹软链接
  • 原文地址:https://www.cnblogs.com/DukeLv/p/9478497.html
Copyright © 2011-2022 走看看