zoukankan      html  css  js  c++  java
  • 其他OJ 树型DP 选课

    在朱全民的PPT介绍的一个树型DP经典题,《选课》,中文题目,不结束

    找了很久找到了可以提交的OJ,重庆八中 http://www.cqoi.net:2012/JudgeOnline/problem.php?id=1376

    简单分析一下:

    1.建树,不要用一般的孩子表示法,这里要讲森林转为二叉树处理才能强劲有力,所以用(左)孩子(右)兄弟法建树

    2.建树之后就可以DP,DP的策略写在代码中了,不多说

    对于树型DP的初步感觉

    1.建树很重要(跟图论里面构图很重要,构图失败基本上整个算法失败了)

    2.树这种结构比较特殊,要找到特殊型入手,一是从属关系,即孩子是属于某个双亲的,二是平行关系,兄弟之间是平行的。因而一个节点的信息往往可以由其孩子的信息得到。同样的,一个节点的信息也有可能传递给孩子

    3.唯一性:一个结点可能有多个孩子,但是双亲一定是唯一的,很多问题的突破就在此

    /*
    用孩子兄弟法建树
    1.对于树的一个节点,分三个域,数据域,第一个孩子域,第一个右兄弟域
    2.接下来每一行读两个信息,j,s。在第i行中,j表示i的先修课程,也就是i是j的孩子
      先把j之前的第一个孩子保存下来,为k,可知i和k是兄弟关系,所以k的第一个右兄弟更新为i
      同时i更新为j的第一个孩子
      更新第一个孩子和第一个右兄弟都用数组来模拟链表实现
      建树后得到的是森林,有一些元素是森林里面的树根,还要设置一个虚拟的节点来作为这些树根的双亲,
      所以建树过程要记录哪些元素是树根,不过还好,输入中已经有了提示,不用另外记录。输入中,树根的双亲都定为0
      这刚好符合我们的要求,我们就把虚拟的节点设为0,同样用孩子兄弟法将森林合并为一棵树
    */
    
    #include <cstdio>
    #include <cstring>
    #define N 310
    #define M 310
    #define INF 0x3f3f3f3f
    #define max(a,b) a>b?a:b
    
    int fc[N],rb[N],data[N]; //第一个孩子,第一个右兄弟,数据域
    int dp[N][M]; //dp[i][j]以i为根,选j门课的最大值
    int n,m;
    
    void build()
    {
        memset(fc,0,sizeof(fc));
        memset(rb,0,sizeof(rb));
        data[0]=0;
        for(int i=1; i<=n; i++)
        {
            int j,k;
            scanf("%d%d",&j,&data[i]);
            k=fc[j]; rb[i]=k; fc[j]=i;
        }
    }
    
    int dfs(int i ,int j)
    {
        if(i==0 || j==0) return dp[i][j]=0;
        if(dp[i][j]!=-1) return dp[i][j];
    
        /*
        dp[i][j]表示以i为根的子树要选择j个课程的最大值(此时的树不是原始的森林而是孩子兄弟法表示的二叉树)
        dp策略为:对于当前节点,其左指针是它的第1个孩子,右指针是它的第一个兄弟,左边是从属关系,右边是平行关系
                  如果不选当前节点,那么它整个左子树都不能选了,因为整个左子树都是它的后代,
                  那么将由它的右子树分担选j个课程的责任(左边从属关系,右边平行关系,不受当前节点影响)
                  如果选了当前节点,那么剩下的课程就是j-1个,这时候即可以到左子树继续选,也可以到右子树继续选
                  且要满足两棵子树选课之和为j-1
        */
        dp[i][j]=0;
        int c=fc[i] , b=rb[i];
        //选当前节点,那么孩子和兄弟都要考虑
        for(int k=0; k<j; k++) //枚举左边分担的个数
        {
            dfs(c,k);
            dfs(b,j-1-k);
            dp[i][j] = max(dp[i][j] , data[i]+dp[c][k]+dp[b][j-1-k]);
        }
        //不选当前节点,那么只能从它的兄弟开始选,但是它必须得有兄弟
        dfs(b,j);
        dp[i][j] = max(dp[i][j] , dp[b][j]);
    
        return dp[i][j];
    }
    
    void solve()
    {
        memset(dp,-1,sizeof(dp));
        dfs(fc[0],m);
        printf("%d\n",dp[fc[0]][m]);
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            build(); //建树
            solve();
        }
        return 0;
    }
  • 相关阅读:
    GNU make manual 翻译(九十九)
    GNU make manual 翻译( 九十五)
    Shell的 for 循环小例子
    makefile中对目录遍历的小例子
    GNU make manual 翻译(九十三)
    GNU make manual 翻译( 一百)
    GNU make manual 翻译( 九十七)
    GNU make manual 翻译( 九十八)
    mapserver4.8.3 的readme.win32的中文翻译文件
    遥控器编程
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2979417.html
Copyright © 2011-2022 走看看