• 【P1304】【P1305】选课与选课输出方案


    多叉树归

    原题:

    学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N<500)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。

      在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。

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

    (1≤N≤500)

    思路根上一个差不多,不过这里可以把多叉转成2叉,用左儿子右兄弟

    所以这里f[x][y]的意义有点不一样,表示涉及到x,x的兄弟,x的儿子的方案的最优值

    这样搞的话搞法就是先搞一下兄弟,表示不选x,然后把f[x][y]的初值设成f[brother[x]][y],然后再搞兄弟和儿子一起搞的最优值

    枚举i,先搞f[brother][i],再搞f[child[x]][y-i-1],然后更新答案

    需要设置一个超级根,f[x][y]表示的是上面说的意义↑的话,需要从child[root]开始搞

    也可以直接搞多叉的,比较直观,不过似乎不好写

    怎么搞方案呐

    似乎DP都是这么搞方案的:如果当前阶段的值等于最优值在这个阶段的值(求最优值的时候存下来了),这个阶段就在方案中

    具体到这道题上,首先如果f[x][y]==f[brother[x]][y],说明x没用,直接搞brother[x],为什么看上面的求法↑

    然后枚举i,如果f[x][y]==f[brother[x]][i]+f[child[x]][y-i-1]+value[x],就进去搞brother[x],i和child[x],y-i-1

    直接贴输出方案的代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 int read(){int z=0,mark=1;  char ch=getchar();
     8     while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;  ch=getchar();}
     9     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
    10     return z*mark;
    11 }
    12 struct dcd{int child,brother,value;}tree[510];
    13 inline void insert_tree(int x,int y){
    14     if(!tree[x].child)  tree[x].child=y;
    15     else{  tree[y].brother=tree[x].child;  tree[x].child=y;}
    16 }
    17 int n,m;
    18 int f[510][510];
    19 bool have[510];
    20 void DP_tree(int x,int y){
    21     if(f[x][y])  return ;
    22     if(x==0)  return ;  if(y==0){  f[x][y]=0;  return ;}
    23     DP_tree(tree[x].brother,y);//不选x,去搞brother
    24     f[x][y]=max(f[x][y],f[tree[x].brother][y]);//别忘了还可以不选x选brother,这个要先处理一下
    25     for(int i=0;i<y;i++){
    26         DP_tree(tree[x].brother,i);//搞brother,选i个
    27         DP_tree(tree[x].child,y-i-1);//搞child
    28         f[x][y]=max(f[x][y],f[tree[x].brother][i]+f[tree[x].child][y-i-1]+tree[x].value);
    29     }
    30     return ;
    31 }
    32 void get_have(int x,int y){
    33     if(x==0 || y==0)  return ;
    34     if(f[x][y]==f[tree[x].brother][y])  get_have(tree[x].brother,y);
    35     else
    36         for(int i=0;i<y;i++)if(f[x][y]==f[tree[x].brother][i]+f[tree[x].child][y-i-1]+tree[x].value){
    37             get_have(tree[x].brother,i);  get_have(tree[x].child,y-i-1);
    38             have[x]=true;
    39             return ;
    40         }
    41 }
    42 int main(){//freopen("ddd.in","r",stdin);
    43     memset(tree,0,sizeof(tree));
    44     memset(have,0,sizeof(have));
    45     cin>>n>>m;
    46     int _father;
    47     for(int i=1;i<=n;i++){
    48         _father=read();  if(!_father)  _father=n+1;  insert_tree(_father,i);//不设成0是因为还要判空
    49         tree[i].value=read();
    50     }
    51     DP_tree(tree[n+1].child,m);//不能从n+1开始,因为上面状态转移↑设定的是f[x][y]表示x自己和儿子和兄弟一起搞的最大值
    52     cout<<f[tree[n+1].child][m]<<endl;
    53     get_have(tree[n+1].child,m);
    54     for(int i=1;i<=n;i++)if(have[i])  cout<<i<<endl;
    55     cout<<endl;
    56     return 0;
    57 }
    View Code
  • 相关阅读:
    软件工程个人作业(4)
    软件工程个人作业(3)
    软件工程个人作业(2)
    软件工程个人作业(1)
    构建之法
    消息分发机制,实现战场与UI通信功能
    设置父物体方法(包括层级)
    NGUI通过点击按钮来移动面板位置,实现翻页功能
    unity中调用android的剪切板
    屏蔽UI事件
  • 原文地址:https://www.cnblogs.com/JSL2018/p/5845513.html
Copyright © 2020-2023  润新知