• 树形DP 洛谷P2014 选课


    洛谷P2014 选课

    题目描述

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

    输入输出格式

    输入格式:

    第一行有两个整数N,M用空格隔开。(1<=N<=300,1<=M<=300)

    接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。

    输出格式:

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

    好困啊~~~

    这道题做的不好。

    一开始的做法:
    ​ 设(f(i)(j))表示在以i为根的子树中,选择j个点并且一定选择i的最大价值。

    ​ 状态倒是设对了。但是转移就不会了。

    ​ 想的是树上的分组背包。

    ​ 将当前点的每个儿子看成一类物品,然后就可以枚举这一类物品中选多少个。

    ​ 应该是没问题,但是太困写不动了。。。

    ​ 而且我犯了一个错误就是对于当前的点u,应该枚举一层这个点的子树中选择多少个,所以我们应该记录当前的这个数的大小,并用其作为上界,但我是写了一个记忆化搜索每一次将传入的参数t当做上界,就不对了。

    好了,感觉我的做法思路很乱套。

    开始说一说正确做法吧。

    ​ 状态很好设:设(f(i)(j))表示在以i为根的子树中,选择j个点并且一定选择i的最大价值。

    ​ 重点是怎么去推。

    ​ 这里用到了一个东西叫做多叉转二叉。也就是说我们要记录当前节点的子树大小作为枚举上界,所以我们可以每一次将当前点现在所枚举到的子树的大小加上,就是开一个变量记录当前的子树大小,在转移之前先将当前枚举到的子节点的size加上。

    ​ 这里有一个DP的思想,个人感觉类似记忆化搜索吧,挺重要的,就是我们当前要用v去转移u的状态,那么肯定的,我们应该知道v的状态(当然也可以在转移时记忆化搜索出v的状态),所以我们在转移u之前就应该先将v转移好。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int wx=3017;
    inline int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
    	return sum*f;
    }
    struct e{
    	int nxt,to;
    }edge[wx*2];
    int n,m,num,x;
    int head[wx],a[wx],f[317][317],size[wx];
    void add(int from,int to){
    	edge[++num].nxt=head[from];
    	edge[num].to=to;
    	head[from]=num;
    }
    int dfs(int u){
    	f[u][1]=a[u];int tot=1;
    	for(int i=head[u];i;i=edge[i].nxt){
    		int v=edge[i].to;tot+=dfs(v);
    		for(int j=tot;j>=1;j--){
    			for(int k=1;k<j;k++){
    				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
    			}
    		}
    	}
    	return tot;
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++){
    		x=read();a[i]=read();
    		add(x,i);
    	}
    	dfs(0);
    	printf("%d
    ",f[0][m+1]);
    	return 0;
    }
    //f[i][j]表示在以i为根的子树中选择j个点并且一定选i这个点的最大价值 
    //f[i][j]=max(f[i][j],f[v][k]+a[v]+f[i][j-k])
    
  • 相关阅读:
    css之深入理解padding
    css布局大杂烩
    css深入理解margin
    css之深入理解border
    css样式画各种图形
    css Sprite雪碧图
    JVM,JRE,JDK
    JAVA 遍历数组
    JAVA 得到数组的长度
    大一对软件工程
  • 原文地址:https://www.cnblogs.com/wangxiaodai/p/9762448.html
Copyright © 2020-2023  润新知