hihocoder #1104 : Suzhou Adventure

题目描述:http://hihocoder.com/problemset/problem/1104

这道题用树形dp来解答,第一种方法容易想到,用dp[i][j]来表示以i为根节点的子树中恰好访问j个节点所获得的最大score,并且这j个节点中必须包含子树中所有必须访问到的节点。注意:如果一个节点是推荐节点,那么它的父节点也必须被访问。

第一层循环,计算出子树i中必须被访问的节点,并且用temp来记录访问这些必要节点所得到的score,且temp还包括节点i的score,这样,如果子树中没有推荐节点,temp就是i的score;然后dp[i][must[i]]=temp,若没有推荐节点,那么dp[i][1]=temp;dp[i][<must[i]]=-1,表示这些情况不可能,因为要访问全部必要节点,至少要访问must[i]个节点。

第二层循环就是通过枚举每一个子节点来计算在访问了必要节点的情况下访问j个节点得到的的最大score,注意,对于must[v]不为零的子节点,也要枚举,因为除了子树v中的必要节点外,还可以再访问其他节点,还有,求解时要减去dp[v][must[v]],在这里改了好多次,好在最后AC了。

#include <stdio.h>
#include <string.h>
#define MAX_N 100

struct EDGE{
	int to, next;
}e[MAX_N*2];

//fe[]记录每个节点的第一个子节点,sco[]记录score,isRec[]记录是否recommended 
//dp[i][j]表示以i为根节点的子树中恰好访问j个节点且rec节点全部访问所获得的最大score 
int fe[MAX_N+1], n, m, k, sco[MAX_N+1], isRec[MAX_N+1], dp[MAX_N+1][MAX_N+1];
//must[i]记录以i为根节点的子树中必须访问的节点的个数 
int must[MAX_N+1]; 

void Add(int a, int b, int eCount){
	e[eCount].to = b;
	e[eCount].next = fe[a];
	fe[a] = eCount;
}

int max(int a, int b){
	return a > b ? a : b;
}

void dfs(int u, int fa){
	int i, v, temp, x, y, yy;
	must[u] = isRec[u];
	
	//计算以i为根节点的子树中必须访问的节点的个数,并计算temp=这些节点的score之和 
	temp = sco[u];
	for(i = fe[u]; i+1; i = e[i].next){
		v = e[i].to;
		if(v != fa){
			dfs(v, u);
			if(must[v]){
				must[u] += must[v];
				temp += dp[v][must[v]];
			}
		}
	}
	if(!isRec[u] && must[u]){
		must[u]++;
	}
	//如果有rec节点,则访问这些节点可获得相应的score,否则,temp为节点i的score 
	if(must[u]){
		dp[u][must[u]] = temp;
	}else{
		dp[u][1] = temp;
	}
	
	for(i = fe[u]; i+1; i = e[i].next){
		v = e[i].to;
		if(v != fa){
			for(x = m; x > must[u]; x--){
				for(y = must[v]+1; y < x; y++){
					if(dp[u][x-y+must[v]] != -1 && dp[v][y] != -1){
						if(must[v]) temp = dp[v][must[v]];
						else temp = 0;
						dp[u][x] = max(dp[u][x], dp[u][x-y+must[v]]+dp[v][y]-temp);
					}
				}
			}
		}
	}
}

int main(){
	int i, a, b, r, eCount;
	
	scanf("%d%d%d", &n, &k, &m);
	for(i = 1; i <= n; i++){
		scanf("%d", &sco[i]);
	}
	memset(isRec, 0, sizeof(isRec));
	for(i = 0 ;i < k; i++){
		scanf("%d", &r);
		isRec[r] = 1;
	}
	memset(fe, -1, sizeof(fe));
	for(eCount = 0, i = 1; i < n; i++){
		scanf("%d%d", &a, &b);
		Add(a, b, eCount);
		eCount++;
		Add(b, a, eCount);
		eCount++;
	}
	
	memset(dp, -1, sizeof(dp));
	dfs(1, -1);
	
	printf("%d\n", dp[1][m]);
	return 0;
} 

第二种方法比较机智

参见这篇文章:http://blog.csdn.net/wsjingping/article/details/45889615

其中有一个错误的地方,就是判断是否有解的时候,不能简单的比较k<=m,因为要访问推荐节点,还必须访问它的父节点,所以应判断dp[1][m]是否小于等于零,然则输出-1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值