树上背包O(n*m^2)|| 多叉树转二叉树 || o(n*m)???

#485. 选课

    描述
    提交
    自定义测试

问题描述

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
输入格式

第一行有两个整数N,M用空格隔开。(1<=N<=200,1<=M<=150) 接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
输出格式

只有一行,选M门课程的最大得分。
样例输入

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

样例输出

13

限制与预定

时间限制:1s

空间限制:128mb

 maybe right:

#include<cstdio>
using namespace std;
const int maxn=1e3+5;
const int maxm=2e3+5;
const int INF=1e9;
inline int max(int a,int b){
    return a < b ? b : a ;
}
inline void read(int &a){
a=0;int b=1;char x=getchar();
while(x<'0'||'9'<x){if(x=='-')b=-1;x=getchar();}
while('0'<=x&&x<='9'){a=(a<<3)+(a<<1)+x-'0';x=getchar();}
a*=b;
}
int n,m;
int first[maxn],next[maxn],to[maxn],w[maxn],edge_count;
inline void add(int x,int y){
    edge_count++;
    to[edge_count]=y;
    next[edge_count]=first[x];
    first[x]=edge_count;
}
int f[maxn][maxm];
void search(int root){
    //printf("%d:\n",root);
    //f[root][0]=0;
    //f[root][1]=w[root];
    for(int i=first[root];i;i=next[i]){
        search(to[i]);
        //if(to[i]==2)printf("%d",root);
        for(int j=m+1;j;j--){
        for(int k=0;k<j;k++){
        f[root][j]=max(f[root][j],f[root][j-k]+f[ to[i] ][k]);//转移方程:基于分组背包(泛化背包)
        }
    }
}
for(int i=1;i<=m+1;i++)f[root][i]+=w[root];//当前背包内没有放入root结点,最后加上
//for(int i=1;i<=m+1;i++)printf("f[%d][%d]%d ",root,i,f[root][i]);putchar('\n');
}
int main()
{
    read(n);read(m);
    
    //for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)f[i][j]=-INF;
    
    for(int i=1,fa,c;i<=n;i++){
        read(fa);read(w[i]);
        add(fa,i);
    }
    search(0);//以虚拟结点为根搜索
    printf("%d",f[0][m+1]);//0号结点不能选,所以输出f【0】【m+1】
    return 0;
}

 

posted @ 2019-04-11 20:39  Tj1  阅读(222)  评论(0编辑  收藏  举报