5788. 【NOIP提高A组模拟2018.8.9】餐馆

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值