农夫有n块农田,农田里种满了稻子。秋天到了,稻子熟了,每块农田都有一定数量的稻子。我们可以把农田看成n个点,编号是1到n。农夫起点编号是1。恰好有n-1条道路连接这些点,每条道路长度都为1,并且任意2点都是可达的。每条道路都有一定的长度。现在农夫从起点出发,到农田收割稻子。农夫每经过一块农田就能收割该农田里的稻子。但是农夫是如此的懒惰,他可不想走过的总路程超过m。农夫应该如何选择一种收割方案使得到的稻子最多。农夫最后可以停在任意点!
Input
第一行一个正整数n(1<=n<=100)表示农田数;
第二行n个整数(不大于1000)表示每块农田的稻子数。
接着n-1行每行两个整数a,b,表示a和b之间有一条长度为1的道路,道路是双向的。
最后一行一个整数m(0<=m<=200)表示农夫最多走m的路程。
2
1 1
1 2
1
Output
输出一个整数,表示农夫能得到最多的稻子。
2
根据题目的数据范围,n<=100,m<=200,想到树形dp。
令1为根,设g[i][j]表示在i为根的子树中,以i为起点,最多走j的路程,得到的最多麦子数。
ans=g[1][m]
但只设一个并不能写出转移方程,比如节点i,它可能走向一个儿子y1,然后返回到 i,再走向另一个儿子y2。
所以这里设一个辅助用的数组f
设f[i][j]表示在i为根的子树中,以i为起点,最多走j的路程,又回到了 i,得到的最多麦子数。
令y为i的一个儿子
方程: (走y后又回来) g[i][j]=max(g[i][j],g[i][j-k-2]+f[y][k]);
(走y后不回来)g[i][j]=max(g[i][j],f[i][j-k-1]+g[y][k]);
f[i][j]=max(f[i][j],f[i][j-k-2]+f[y][k]);
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 using namespace std; 8 int n,m,cnt; 9 int h[105]; 10 int v[105]; 11 int f[105][205],g[105][205]; 12 struct node{int to,next;}a[205]; 13 void add(int x,int y){cnt++;a[cnt].to=y;a[cnt].next=h[x];h[x]=cnt;} 14 void dp(int x,int fa) 15 { 16 int i,j,k,y; 17 for(i=0;i<=m;i++)f[x][i]=g[x][i]=v[x]; 18 for(i=h[x];i;i=a[i].next) 19 { 20 y=a[i].to; 21 if(y==fa)continue; 22 dp(y,x); 23 for(j=m;j>=0;j--) 24 for(k=0;k<=j-2;k++) 25 g[x][j]=max(g[x][j],g[x][j-k-2]+f[y][k]); 26 for(j=m;j>=0;j--) 27 for(k=0;k<=j-1;k++) 28 g[x][j]=max(g[x][j],f[x][j-k-1]+g[y][k]); 29 for(j=m;j>=0;j--) 30 for(k=0;k<=j-2;k++) 31 f[x][j]=max(f[x][j],f[x][j-k-2]+f[y][k]); 32 } 33 } 34 int main() 35 { 36 int n,x,y,i; 37 cin>>n; 38 for(i=1;i<=n;i++)scanf("%d",&v[i]); 39 for(i=1;i<=n-1;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); 40 cin>>m; 41 dp(1,0); 42 cout< 1][m]; 43 return 0; 44 }