• 刷题总结——选课(ssoj树形dp+记忆化搜索+多叉树转二叉树)


    题目:

    题目描述

    学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了 N(N<300)门的选修课程,每个学生可选课程的数量 M 是给定的。学生选修了这M门课并考核通过就能获得相应的学分。
    在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。 例如:

    课号 先修课号 学分
    1 1
    2 1 1
    3 2 3
    4 3
    5 2 4

    表中 1 是 2 的先修课,2 是 3、4 的先修课。如果要选 3,那么 1 和 2 都一定已被选修过。

    你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

    输入格式

    输入文件的第一行包括两个整数 N、M(中间用一个空格隔开)其中 1≤N≤300,1≤M≤N。
    以下N行每行代表一门课。课号依次为 1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过 10 的正整数。

    输出格式

    输出文件只有一个数,实际所选课程的学分总数。

    样例数据 1

    输入  [复制]

     
    7 4 
    2 2 
    0 1 
    0 4 
    2 1 
    7 1 
    7 6 
    2 2

    输出

    13

    题解:

      引用ssoj官方题解:

      用孩子兄弟法建树 1.对于树的一个节点,分三个域,数据域,第一个孩子域,第一个右兄弟域 2.接下来每一行读两个信息,j,s。在第i行中,j表示i的先修课程,也就是i是j的孩子 先把j之前的第一个孩子保存下来,为k,可知i和k是兄弟关系,所以k的第一个右兄弟更新为i 同时i更新为j的第一个孩子 更新第一个孩子和第一个右兄弟都用数组来模拟链表实现 建树后得到的是森林,有一些元素是森林里面的树根,还要设置一个虚拟的节点来作为这些树根的双亲, 所以建树过程要记录哪些元素是树根,不过还好,输入中已经有了提示,不用另外记录。输入中,树根的双亲都定为0 这刚好符合我们的要求,我们就把虚拟的节点设为0,同样用孩子兄弟法将森林合并为一棵树

      然后用做苹果二叉树的方法来做就行了····(苹果二叉树题解见我博客)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    using namespace std;
    const int N=305;
    int val[N],brother[N],son[N],n,m;
    int dp[N][N];
    inline void dfs(int u,int k)
    {
      if(u==0||k==0)
      {
        dp[u][k]=0;
        return;
      }
      if(dp[u][k]!=-1)  return; 
      dp[u][k]=0;
      for(int i=0;i<k;i++)
      {
        dfs(son[u],i);
        dfs(brother[u],k-i-1);
        dp[u][k]=max(dp[u][k],dp[son[u]][i]+dp[brother[u]][k-i-1]+val[u]);
      }
      dfs(brother[u],k);
      dp[u][k]=max(dp[u][k],dp[brother[u]][k]);
      return;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      memset(dp,-1,sizeof(dp));
      scanf("%d%d",&n,&m);
      int a,b;
      for(int i=1;i<=n;i++) 
      {
        scanf("%d%d",&a,&b);
        val[i]=b;
        brother[i]=son[a];
        son[a]=i;
      }
      dfs(son[0],m);
      cout<<dp[son[0]][m]<<endl;
      return 0;
    }
  • 相关阅读:
    怎么查看京东店铺的品牌ID
    PPT编辑的时候很卡,放映的时候不卡,咋回事?
    codevs 1702素数判定2
    codevs 2530大质数
    codevs 1488GangGang的烦恼
    codevs 2851 菜菜买气球
    hdu 5653 Bomber Man wants to bomb an Array
    poj 3661 Running
    poj 1651 Multiplication Puzzle
    hdu 2476 String Painter
  • 原文地址:https://www.cnblogs.com/AseanA/p/7400406.html
Copyright © 2020-2023  润新知