题目链接: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 ",ans); 77 } 78 }