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;
    }
  • 相关阅读:
    边缘引导插值/方向卷积插值
    cout显示Mat类对象报错Access Violation
    图像特征点匹配C代码
    TF-IDF(词频-逆向文件频率)用于文字分类
    Jsp中如何通过Jsp调用Java类中的方法
    根据wsdl文件,soupUI生成webservice客户端代码
    根据wsdl,axis2工具生成客户端代码
    根据wsdl,apache cxf的wsdl2java工具生成客户端、服务端代码
    根据wsdl,基于wsimport生成代码的客户端
    Mysql截取和拆分字符串函数用法
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2979417.html
Copyright © 2011-2022 走看看