加分二叉树(Treedp)

题目描述

【问题描述】
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有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

 

题解:

首先要把多叉树转换为二叉树 :遵循原则:左节点为儿子,右节点为兄弟。d[x],l[],r[]分别表示 x结的最远儿子节点,左节点右节点。

 

inline void ins(int x,int f){//x为子节点,f为父节点 
	if( !d[f] ) l[f]=x;
	else r[d[f]]=x;
	d[f]=x; 
} 

 

题目要选若干点使得分最大,若选子节点就必须选父节点,将多叉树转为二叉树后,问题就转换为 以x为节点保留y个节点的最大值。

所以设 

f[x][y] 以x为根保留y个节点的最大值 f[x][y]=max{    f[ r[x] ][ y ]  ,   f[ l[x]

][k]+f[r[x]][y-k-1]+c[x]  } k=[0,y-1 ]

f[0][m+1] 即为答案。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1100;
int f[MAXN][MAXN],c[MAXN],l[MAXN],r[MAXN],d[MAXN];
int n,m;

inline void ins(int x,int f){//x为子节点,f为父节点 
	if( !d[f] ) l[f]=x;
	else r[d[f]]=x;
	d[f]=x; 
} 

int dp(int x,int y){
	if(x<0 || y<0) return 0;
	if(f[x][y]!=-1) return f[x][y];
	int maxn=0;
	maxn=dp(r[x],y);
	for(int i=0;i<=y-1;i++){
		int ls=y-i-1;int rs=i;
		int lss=0,rss=0;
		if(l[x]!=-1) lss=f[l[x]][ls]=dp(l[x],ls);
		if(r[x]!=-1) rss=f[r[x]][rs]=dp(r[x],rs);
	//	if(ls<0) maxn=max(maxn,rss); 
		maxn=max(maxn,lss+rss+c[x]);
	}
	
	return maxn;
}


int main(){
//	freopen("caioj1108.in","r",stdin);
	scanf("%d%d",&n,&m);
	memset(l,-1,sizeof l);memset(r,-1,sizeof r);
	
	for(int i=1,x;i<=n;i++){
		scanf("%d%d",&x,&c[i]);
		ins(i,x);
	}
	
	memset(f,-1,sizeof f);
	for(int i=0;i<=n;i++)f[i][0]=0; c[0]=0;
	printf("%d\n",dp(0,m+1));
	
	return 0;
}

 34234

posted @ 2018-10-18 22:54  Exception2017  阅读(197)  评论(0编辑  收藏  举报