zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    js正则表达式中的问号使用技巧总结
    380. Insert Delete GetRandom O(1)
    34. Find First and Last Position of Element in Sorted Array
    162. Find Peak Element
    220. Contains Duplicate III
    269. Alien Dictionary
    18. 4Sum
    15. 3Sum
    224. Basic Calculator
    227. Basic Calculator II
  • 原文地址:https://www.cnblogs.com/JSL2018/p/5845513.html
Copyright © 2011-2022 走看看