zoj 3506 Cut the Tree (树形dp)

这道题细节很多,调了很长时间,设mx[u][j]表示以u为根的子树,切j刀后,这颗子树中与u连通的所有节点的最大权值,包括u。考虑每颗子树的决策,要么切掉,要么连上。
附用例:
4 1
-3 -2 1 1
1 2
2 3
1 4

4 2
-3 -2 1 1
1 2
2 3
1 4

5 2
1 2 3 4 5
1 2
2 3
3 4
4 5

6 2
-3 1 -2 3 4 0
1 2
1 3
2 4
3 5
3 6

6 3
-3 1 -2 3 4 0
1 2
1 3
2 4
3 5
3 6

6 4
-3 1 -2 3 4 0
1 2
1 3
2 4
3 5
3 6

6 5
-3 1 -2 3 4 0
1 2
1 3
2 4
3 5
3 6

4 2
1 2 3 4
1 2
2 3
3 4

10 4
-1 2 -3 4 -5 6 -7 8 -9 10
1 2
2 4
2 5
2 6
1 3
3 8
3 7
7 9
7 10

10 8
-1 2 -3 4 -5 6 -7 8 -9 10
1 2
2 4
2 5
2 6
1 3
3 8
3 7
7 9
7 10

ans:
-4 1
-5 1
1 12
-5 4
-5 4
-5 4
-3 4
1 7
-23 16
-16 10

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 1005
using namespace std;

vector<int>edge[N];
int weight[N],mx[N][25],mn[N][25],n,k,ansmx,ansmn;

int dfs(int u,int fa)
{
        int i,len=edge[u].size(),v,j,l,sum=0,nums;
        mn[u][0]=mx[u][0]=weight[u];
        for(i=0;i<len;i++){
                v=edge[u][i];
                if(v==fa) continue;
                if(u==2)
                u=2;
                sum+=(nums=dfs(v,u));
                for(j=min(k,sum);j>=0;j--){
                        int tmp1=-999999999,tmp2=999999999;
                        for(l=0;l<j && l<nums;l++){
                                tmp1=max(tmp1,max(mx[u][j-l-1],mx[u][j-l]+mx[v][l]));
                                tmp2=min(tmp2,min(mn[u][j-l-1],mn[u][j-l]+mn[v][l]));
                        }
                        if(l<nums && j==l) tmp1=max(tmp1,mx[u][j-l]+mx[v][l]) , tmp2=min(tmp2,mn[u][j-l]+mn[v][l]);
                        mx[u][j]=tmp1;
                        mn[u][j]=tmp2;
                }
        }
       for(j=(u!=1);j<=k && j<=n-sum-1;j++){
                ansmx=max(ansmx,mx[u][k-j]);
                ansmn=min(ansmn,mn[u][k-j]);
       }
        return sum+1;
}

int main()
{
        while(~scanf("%d%d",&n,&k)){
                memset(mx,-50,sizeof(mx));
                memset(mn,50,sizeof(mn));
                ansmx=mx[0][0]; ansmn=mn[0][0];
                int i;
                for(i=1;i<=n;i++) edge[i].clear();
                for(i=1;i<=n;i++) scanf("%d",weight+i);
                for(i=1;i<n;i++){
                        int a,b;
                        scanf("%d%d",&a,&b);
                        edge[a].push_back(b);
                        edge[b].push_back(a);
                }
                dfs(1,0);
                printf("%d %d\n",ansmn,ansmx);
        }
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值