2010福建 收稻子

Description

  农夫有n块农田,农田里种满了稻子。秋天到了,稻子熟了,每块农田都有一定数量的稻子。我们可以把农田看成n个点,编号是1n。农夫起点编号是1。恰好有n-1条道路连接这些点,每条道路长度都为1,并且任意2点都是可达的。每条道路都有一定的长度。现在农夫从起点出发,到农田收割稻子。农夫每经过一块农田就能收割该农田里的稻子。但是农夫是如此的懒惰,他可不想走过的总路程超过m。农夫应该如何选择一种收割方案使得到的稻子最多。农夫最后可以停在任意点!

Input

  第一行一个正整数n1<=n<=100)表示农田数;
  第二行n个整数(不大于1000)表示每块农田的稻子数。
  接着n-1行每行两个整数a,b,表示ab之间有一条长度为1的道路,道路是双向的。
  最后一行一个整数m0<=m<=200)表示农夫最多走m的路程。

Output

  输出一个整数,表示农夫能得到最多的稻子。

Sample Input

2

1 1

1 2

1

Sample Output

2

md考试的时候脑子一抽数组开小了。。。

这是一道树上背包的一类模板题。写的时候注意几个细节:

1、更新的时候从大到小(嗯,背包的做法)

2、注意处理的顺序,保证用第i棵子树时,前i-1的答案不会被重复优化

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
struct Edge{
	int cnt,h[N],to[N<<1],next[N<<1];
	inline void add(int x,int y){
		next[++cnt]=h[x];
		to[cnt]=y;
		h[x]=cnt;
	}
}e;
int n,m,a[N];
inline void init(){
	scanf("%d",&n);
	for(register int i=1;i<=n;++i)scanf("%d",&a[i]);
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		e.add(x,y),e.add(y,x);
	}
	scanf("%d",&m);
}
int f[110][210];//以i为根, 
int g[110][210];//不回到根 
#define to e.to[p]
void dp(int x,int fa){
	f[x][0]=g[x][0]=a[x];
	for(int p=e.h[x];p;p=e.next[p])if(to^fa){
		dp(to,x);
		for(int i=m;i>0;--i)
			for(int j=0;i-j-2>=0;++j)
				f[x][i]=max(f[x][i],g[to][j]+f[x][i-j-2]);
		for(int i=m;i>0;--i)
			for(int j=0;i-j-1>=0;++j)
				f[x][i]=max(f[x][i],g[x][j]+f[to][i-j-1]);
		for(int i=m;i>0;--i)
			for(int j=0;i-j-2>=0;++j)
				g[x][i]=max(g[x][i],g[x][j]+g[to][i-j-2]);
	}
	for(int i=1;i<=m;++i)
		f[x][i]=max(f[x][i],f[x][i-1]),g[x][i]=max(g[x][i],g[x][i-1]);
}
int main(){
	init();
	dp(1,0);
	cout<<f[1][m]<<"\n";
	return 0;
}
//5
//10 11 32 41 35
//1 2
//1 3
//2 5
//2 4
//10

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值