ZOJ 3188 ZOJ 3188 Treeland Exhibition(树形DP)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3278

题意:给出一棵树,找出一个不大于长度为m的链,使得其他点到该链的长度之和最小。

预处理出以下数组:

(1)downSum[u]:u的所有孩子到达u的距离,downCnt[u]:u子树节点个数;

(2)upSum[u]:除u子树的节点外其他节点到达u的距离;upCnt[u]:除u子树的节点个数。

树形DP时,竖线形状的比较简单,我们用dp[u][L]表示以u为子树,向下有一条长度为L的链的最小代价;然后dp[u][m]+upSum[u]就是答案;对于折线形状的,设以u的两个孩子v1、v2组成,则答案为upSum[u]+downSum[u]+dp[v1][j-1]-downSum[v1]-downCnt[v1]+dp[v2][m-1-j]-downSum[v2]-downCnt[v2],因此只要维护upSum[u]+downSum[u]+dp[v1][j-1]-downSum[v1]-downCnt[v1]的最小值即可。

 1 #include<algorithm>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<iostream>
 6 int ans,downcnt[100005],downsum[100005];
 7 int upsum[200005],upcnt[200005];
 8 int n,m;
 9 int tot,go[200005],first[200005],next[200005];
10 int dp[10005][105],f[10005][105];
11 void insert(int x,int y){
12     tot++;
13     go[tot]=y;
14     next[tot]=first[x];
15     first[x]=tot;
16 }
17 void add(int x,int y){
18     insert(x,y);insert(y,x);
19 }
20 void dfs(int x,int fa){
21     downcnt[x]=1;
22     downsum[x]=0;
23     upcnt[x]=0;
24     upsum[x]=0;
25     for (int i=first[x];i;i=next[i]){
26         int pur=go[i];
27         if (pur==fa) continue;
28         dfs(pur,x);
29         downcnt[x]+=downcnt[pur];
30         downsum[x]+=downsum[pur]+downcnt[pur];
31     }
32     upcnt[x]=n-downcnt[x];
33 }
34 void Dfs(int x,int fa){
35     if (fa!=0){
36         upcnt[x]=n-downcnt[x];
37         upsum[x]=upsum[fa]+upcnt[fa]+downsum[fa]-downsum[x]-downcnt[fa]+1+(downcnt[fa]-1-downcnt[x])*2+1;
38     }else upsum[x]=upcnt[x]=0;
39     for (int i=first[x];i;i=next[i]){
40         int pur=go[i];
41         if (pur==fa) continue;
42         Dfs(pur,x);
43     }
44 }
45 void DP(int x,int fa){
46     for (int i=0;i<=m+1;i++)
47      dp[x][i]=downsum[x],f[x][i]=downsum[x]+upsum[x];
48     for (int i=first[x];i;i=next[i]){
49         int pur=go[i];
50         if (pur==fa) continue;
51         DP(pur,x);
52         for (int j=1;j<=m;j++){
53             dp[x][j]=std::min(dp[x][j],dp[pur][j-1]+downsum[x]-downsum[pur]-downcnt[pur]);
54             if (m-j-1>=0) ans=std::min(ans,f[x][j]+dp[pur][m-1-j]-downsum[pur]-downcnt[pur]);
55         }
56         for (int j=1;j<=m;j++){
57             f[x][j]=std::min(f[x][j],upsum[x]+downsum[x]+dp[pur][j-1]-downsum[pur]-downcnt[pur]);
58         }
59     }
60     ans=std::min(ans,dp[x][m]+upsum[x]);
61 }
62 int main(){
63     while (scanf("%d%d",&n,&m)!=EOF){
64         if (n==0&&m==0) return 0;
65         tot=0;for (int i=1;i<=n;i++) first[i]=0;
66         for (int i=1;i<n;i++){
67             int x,y;
68             scanf("%d%d",&x,&y);
69             x++;y++;
70             add(x,y);
71         }
72         ans=0x7fffffff;
73         dfs(1,0);
74         Dfs(1,0);
75         DP(1,0);
76         printf("%d\n",ans);
77     }
78 }

 

转载于:https://www.cnblogs.com/qzqzgfy/p/5553917.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值