DP-选课(树形DP)

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

  13

解题须知:

顾名思义,树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向: 

    1、叶->根:在回溯的时候从叶子节点往上更新信息

    2、根 - >叶:往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。

    不管是 从叶->根 还是 从 根 - >叶,两者都是根据需要采用,没有好坏高低之分。

分析:在n门课中选出m门课,得到最大的学分。典型的树形DP问题,f[i][j]表示从i中选出j个得到最优解,针对本题可以得到的状态转移方程:

f[i][j]=max(f[i][a],f[k][b])  其中 k是i的子树,a+b=j。

代码:

import java.util.*;
public class 选课 {
	static int[] preCourse=new int[301]; 	 //先选课
	static int[] score=new int[301];     	//每一门课对应的学分
	static int[][] f=new int[301][201];      //f[i][j],表示包括i,选课j门最大学分
	static int m,n;
	public static void TreeDP(int k,int m) {
		//如果选课数为零,终止搜索
		if(m==0)
			return;
		for(int i=1;i<=n;i++) {
			//如果课程k的先选课已经选上了或者是没有先选课
			if(preCourse[i]==k) {
				//记录此时的选课学分
				for(int j=0;j<m;j++) 
					f[i][j]=f[k][j]+score[i];
				//继续搜索
				TreeDP(i,m-1);
				//记录此时选课为k时的学分最大值
				for(int j=1;j<=m;j++)
					f[k][j]=Math.max(f[i][j-1],f[k][j]);
			}
		}
	}
	public static void main(String[] args) {
		Scanner in=new Scanner(System.in);
		String[] s=new String[4];
		n=in.nextInt();
		m=in.nextInt();
		s=in.nextLine().split(" ");
		for(int i=1;i<=n;i++) {
			s=in.nextLine().split(" ");
			preCourse[i]=Integer.parseInt(s[0]);
			score[i]=Integer.parseInt(s[1]);
		}
		//初始化为没选课,并且每门课的学分都为零
		for(int i=0;i<=m;i++) {
			f[0][i]=0;
		}
		TreeDP(0,m);
		//输出选课完成后的最大学分
		System.out.println(f[0][m]);
	}
}

猜你喜欢

转载自blog.csdn.net/zw159357/article/details/79655863