Description
K妹的胡椒粉大卖,这辣味让食客们感到刺激,许多餐馆也买这位K妹的账。有N家餐馆,有N-1条道路,这N家餐馆能相互到达。K妹从1号餐馆开始。每一个单位时间,K妹可以在所在餐馆卖完尽量多的胡椒粉,或者移动到有道路直接相连的隔壁餐馆。第i家餐馆最多需要A[i]瓶胡椒粉。K妹有M个单位的时间,问她最多能卖多少胡椒粉。
Input
第一行有两个正整数N,M。
第二行描述餐馆对胡椒粉的最大需求量,有N个正整数,表示A[i]。
接下来有N-1行描述道路的情况,每行两个正整数u,v,描述这条道路连接的两个餐馆。
Output
一个整数,表示她最多能卖的胡椒粉瓶数。
Sample Input
样例1输入
3 5
9 2 5
1 2
1 3
样例2输入
4 5
1 1 1 2
1 2
2 3
3 4
样例3输入
5 10
1 3 5 2 4
5 2
3 1
2 3
4 2
Sample Output
样例1输出
14
样例2输出
3
样例3输出
15
Data Constraint
对于10%的数据,N≤20。
对于50%的数据,N≤110。
对于100%的数据1 ≤ N, M ≤ 500,1 ≤ A[i]≤ 10^6,
第5到第10个测试点都有多个子测试。
Hint
在样例1的中,辣妹到达城市2后就恰好没时间卖辣椒粉了。
Solution
显然,这是一道树形 dp 的题目。
我们可以设 dp[i][j][0/1]
dp[i][j][0]表示以 i 为根的子树中,花费 j 单位时间,最终回到 i 的最大收益。
dp[i][j][1]表示以 i 为根的子树中,花费 j 单位时间,最终不必回到 i 的最大收益。
边界条件:dp[i][0][0/1]=0; dp[i][1][0/1]=a[i];(a[i]为胡椒粉瓶数)
转移的时候,一颗一颗子树来做。
将已做的子树信息存在 dp[i][ ][ ]里面。
枚举下一颗子树的时候,同时枚举在下一颗子树花费
的时间 k,以及在之前子树花费的时间 j。
那么转移就很显然了。
先走之前的子树回到根,再走下一颗子树不回根:dp[i][j][0] + dp[son][k][1] –> dp[i][j+k+1][1]
先走之前的子树回到根,再走下一颗子树也回根:dp[i][j][0] + dp[son][k][0] –> dp[i][j+k+2][0]
先下一颗子树回到根,再走之前的子树不回根:dp[i][j][1] + dp[son][k][0] –> dp[i][j+k+2][1]
这样的时间复杂度是 O(n^2)的。
证明如下:
我们可以知道假如一颗子树的大小为 size,那么,花
费 3size 的时间就可以遍历完整棵树,所以,一颗子树
的最大时间就是 size 级别的。
两个红色的结点只会在他们的 lca 处一起枚举到并转
移,也就是说,任意两个节点只会在 lca 处对 dp 产生
影响。
所以这个时间复杂度是 O(n^2)的。
Code1
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 520
using namespace std;
int n,m,x,y,ans;
int l[N],nx[N*2],t[N*2],f[N][N][2],a[N];
void add(int x,int y){
t[++t[0]]=y;
nx[t[0]]=l[x];
l[x]=t[0];
}
void dg(int x,int y){
f[x][0][0]=f[x][0][1]=0;
f[x][1][0]=f[x][1][1]=a[x];
for(int k=l[x];k;k=nx[k]){
if(t[k]!=y){
dg(t[k],x);
int son=t[k];
for(int i=m;i>=0;i--){
for(int j=m-i;j>=0;j--){
if(i+j+1<=m) f[x][i+j+1][0]=max(f[x][i+j+1][0],f[x][i][1]+f[son][j][0]);
if(i+j+2<=m) f[x][i+j+2][1]=max(f[x][i+j+2][1],f[x][i][1]+f[son][j][1]);
if(i+j+2<=m) f[x][i+j+2][0]=max(f[x][i+j+2][0],f[x][i][0]+f[son][j][1]);
}
}
}
}
}
int main(){
freopen("dostavljac.in","r",stdin);
freopen("dostavljac.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dg(1,0);
printf("%d",max(f[1][m][1],f[1][m][0]));
return 0;
}
Code2
uses math;
var
n,m,T,i,j,k,x,y,tot,ans:longint;
a:array[0..500] of longint;
f:array[0..500,-100..500,0..1] of longint;
tov,pre,last:array[0..1000] of longint;
procedure insert(x,y:longint);
begin
inc(tot);
tov[tot]:=y;
pre[tot]:=last[x];
last[x]:=tot;
end;
procedure search(x,q:longint);
var
k,i,j:longint;
begin
k:=last[x];
while k<>0 do
begin
if tov[k]<>q then
begin
search(tov[k],x);
for i:=m downto 0 do
for j:=m downto 0 do
begin
if (i+j+1)<=m then f[x,i+j+1,0]:=max(f[x,i+j+1,0],f[x,i,1]+f[tov[k],j,0]);
if (i+j+2)<=m then f[x,i+j+2,1]:=max(f[x,i+j+2,1],f[x,i,1]+f[tov[k],j,1]);
if (i+j+2)<=m then f[x,i+j+2,0]:=max(f[x,i+j+2,0],f[x,i,0]+f[tov[k],j,1]);
end;
end;
k:=pre[k];
end;
for i:=m downto 0 do
begin
f[x,i+1,0]:=max(f[x,i+1,0],f[x,i,0]+a[x]);
f[x,i+1,1]:=max(f[x,i+1,1],f[x,i,1]+a[x]);
end;
end;
begin
assign(input,'dostavljac.in');reset(input);
assign(output,'dostavljac.out');rewrite(output);
readln(n,m);
for i:=1 to n do read(a[i]);
for i:=1 to n-1 do
begin
readln(x,y);
insert(x,y); insert(y,x);
end;
search(1,0);
for i:=1 to m do ans:=max(ans,f[1,i,0]);
writeln(ans);
end.
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/81545270