树形DP专题
hdu 1520 Anniversary party
分析:要求不跟自己的上司或下属在一起即根不与其孩子同时选择。
dp[i][0]表示i为根且根不选获得的最大值,则其值为孩子结点选择与不选的最大值
即dp[i][0]=max(dp[son][0],dp[son][1]);
dp[i][1]表示i为根且选择获得的最大值,则其孩子结点不能选择dp[i][1]=sum{dp[son][0]};
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 6010 5 using namespace std; 6 struct node 7 { 8 int to,next,w; 9 }edge[maxlen*2]; 10 int head[maxlen]; 11 int num; 12 int dp[maxlen][2]; 13 bool used[maxlen]; 14 int c[maxlen]; 15 void init() 16 { 17 num=0; 18 memset(head,-1,sizeof(head)); 19 memset(used,false,sizeof(used)); 20 memset(dp,0,sizeof(dp)); 21 memset(c,0,sizeof(c)); 22 } 23 void addedge(int u,int v,int w) 24 { 25 edge[num].to=v; 26 edge[num].w=w; 27 edge[num].next=head[u]; 28 head[u]=num++; 29 edge[num].to=u; 30 edge[num].w=w; 31 edge[num].next=head[v]; 32 head[v]=num++; 33 } 34 void dfs(int u) 35 { 36 if(used[u]) 37 return ; 38 used[u]=true; 39 dp[u][1]=c[u]; 40 for(int i=head[u];i!=-1;i=edge[i].next) 41 { 42 int v=edge[i].to; 43 if(!used[v]) 44 { 45 dfs(v); 46 dp[u][0]+=max(dp[v][0],dp[v][1]); 47 dp[u][1]+=dp[v][0]; 48 } 49 } 50 } 51 int main () 52 { 53 int n,x,y; 54 while(scanf("%d",&n)!=EOF) 55 { 56 init(); 57 for(int i=1;i<=n;++i) 58 scanf("%d",&c[i]); 59 while(scanf("%d%d",&x,&y)) 60 { 61 if(x==0&&y==0) 62 break; 63 addedge(x,y,0); 64 } 65 dfs(1); 66 int ans=max(dp[1][0],dp[1][1]); 67 printf("%d\n",ans); 68 } 69 }
hdu 1561 The more, The Better
增加一个权值为0的0号节点使整个成为一棵树,接下来就是0-1背包的问题了
dp[u][j]表示u为根节点选择j个获得的最大价值,根节点必须选择,然后其他可以
从孩子节点中选择。
dp[u][j]=max{dp[u][j],dp[son][j-k]+dp[u][k]}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 210 5 using namespace std; 6 bool used[maxlen]; 7 int cost[maxlen]; 8 int maps[maxlen][maxlen]; 9 int num[maxlen]; 10 int dp[maxlen][maxlen]; 11 int n,m; 12 void dfs(int x) 13 { 14 used[x]=true; 15 for(int i=1;i<=num[x];++i) 16 { 17 int u=maps[x][i]; 18 if(!used[u]) 19 dfs(u); 20 for(int j=m;j>=2;--j) 21 { 22 for(int k=1;k<j;++k) 23 { 24 if(dp[x][k]!=-1&&dp[u][j-k]!=-1&&used[u]) 25 dp[x][j]=max(dp[x][j],dp[x][k]+dp[u][j-k]); 26 } 27 } 28 } 29 30 } 31 int main () 32 { 33 while(scanf("%d%d",&n,&m)!=EOF) 34 { 35 if(n==0&&m==0) 36 break; 37 m++;//添加一个编号为0权值为0的点,这个顶点必须选 38 dp[0][1]=cost[0]=0; 39 memset(maps,0,sizeof(maps)); 40 memset(num,0,sizeof(num)); 41 memset(used,0,sizeof(used)); 42 for(int i=1;i<=n;++i) 43 { 44 int x,y; 45 scanf("%d%d",&x,&y); 46 maps[x][++num[x]]=i; 47 cost[i]=y; 48 } 49 for(int i=0;i<=n;++i) 50 { 51 dp[i][0]=0; 52 dp[i][1]=cost[i]; 53 for(int j=2;j<=m;++j) 54 { 55 dp[i][j]=-1; 56 } 57 } 58 dfs(0); 59 printf("%d\n",dp[0][m]); 60 } 61 }
hdu 2196 Computer
要求输出对于每一个结点的树上任意结点到该点的最大距离。
需要两次dfs,第一次dfs是更新该结点的孩子结点到它的距离,需要记录最大值以及次大值。
另一个dfs是更新从父节点过来的最大值,之所以需要记录次小值是因为如果只存最大值的话,
判断一个点的从父节点过来的最大值,那么如果他的父节点存的最大值正好是从该点过来的,
那么就失去了从父节点过来的状态,所以要记录最大的两个值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define maxlen 10010 6 using namespace std; 7 struct node 8 { 9 int to,next,w; 10 }edge[maxlen*2]; 11 int head[maxlen]; 12 int num; 13 int maxnum[maxlen]; 14 int smaxnum[maxlen]; 15 int maxid[maxlen]; 16 int smaxid[maxlen]; 17 void init() 18 { 19 num=0; 20 memset(head,-1,sizeof(head)); 21 } 22 void addedge(int u,int v,int w) 23 { 24 edge[num].to=v; 25 edge[num].w=w; 26 edge[num].next=head[u]; 27 head[u]=num++; 28 edge[num].to=u; 29 edge[num].w=w; 30 edge[num].next=head[v]; 31 head[v]=num++; 32 } 33 void dfs(int u,int father) 34 { 35 maxnum[u]=smaxnum[u]=0; 36 for(int i=head[u];i!=-1;i=edge[i].next) 37 { 38 int v= edge[i].to; 39 if(v==father) 40 continue; 41 dfs(v,u); 42 if(smaxnum[u]<maxnum[v]+edge[i].w) 43 { 44 smaxnum[u]=maxnum[v]+edge[i].w; 45 smaxid[u]=v; 46 if(smaxnum[u]>maxnum[u]) 47 { 48 swap(smaxnum[u],maxnum[u]); 49 swap(smaxid[u],maxid[u]); 50 } 51 } 52 } 53 } 54 void dfs2(int u,int father) 55 { 56 for(int i=head[u];i!=-1;i=edge[i].next) 57 { 58 int v=edge[i].to; 59 if(v==father) 60 continue; 61 //dfs2(v,u); 62 if(v==maxid[u]) 63 { 64 if(edge[i].w+smaxnum[u]>smaxnum[v]) 65 { 66 smaxnum[v]=edge[i].w+smaxnum[u]; 67 smaxid[v]=u; 68 if(smaxnum[v]>maxnum[v]) 69 { 70 swap(smaxnum[v],maxnum[v]); 71 swap(smaxid[v],maxid[v]); 72 } 73 } 74 } 75 else 76 { 77 if(edge[i].w+maxnum[u]>smaxnum[v]) 78 { 79 smaxnum[v]=edge[i].w+maxnum[u]; 80 smaxid[v] = u; 81 if(smaxnum[v]>maxnum[v]) 82 { 83 swap(smaxnum[v],maxnum[v]); 84 swap(smaxid[v],maxid[v]); 85 } 86 } 87 } 88 dfs2(v,u); 89 } 90 } 91 int main () 92 { 93 int n,v,w; 94 while(scanf("%d",&n)!=EOF) 95 { 96 init(); 97 for(int i=2;i<=n;++i) 98 { 99 scanf("%d%d",&v,&w); 100 addedge(i,v,w); 101 } 102 dfs(1,-1); 103 dfs2(1,-1); 104 for(int i=1;i<=n;++i) 105 printf("%d\n",maxnum[i]); 106 } 107 108 }
hdu 4276 The Ghost Blows Light 2012 ACM/ICPC Asia Regional Changchun Online
分析:首先根据题目意思从1到n的路径是必须要走的,所以我们可以先求出1到n的最短路径然后把这条路径
的权值改为0使接下来的dp一定会选择这些点。
dp[u][j]表示u为根使用j的时间可以获得的最大值,由于最短的路径且这条路径走也只能走一次才能最短,
所以其他的路径都是分支,走过去还是需要回来的,所以花费的时间w是单次的两遍(往返)
方程为dp[u][j] = max{dp[u][j],dp[son][k]+dp[u][j-k-w]} j>=k+w
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 610 5 using namespace std; 6 struct node 7 { 8 int to,next,w; 9 }edge[maxlen*2]; 10 int head[maxlen]; 11 int num; 12 int n,t; 13 int dp[maxlen][maxlen]; 14 int c[maxlen]; 15 bool used[maxlen]; 16 int sum; 17 void init() 18 { 19 num=0; 20 memset(head,-1,sizeof(head)); 21 memset(used,0,sizeof(used)); 22 memset(dp,0,sizeof(dp)); 23 memset(c,0,sizeof(c)); 24 } 25 void addedge(int u,int v,int w) 26 { 27 edge[num].to=v; 28 edge[num].w=w; 29 edge[num].next=head[u]; 30 head[u]=num++; 31 edge[num].to=u; 32 edge[num].w=w; 33 edge[num].next=head[v]; 34 head[v]=num++; 35 } 36 bool short_path(int u,int father) 37 { 38 if(u==n) 39 return true; 40 for(int i=head[u];i!=-1;i=edge[i].next) 41 { 42 int v=edge[i].to; 43 if(v==father) 44 continue; 45 if(short_path(v,u)) 46 { 47 sum+=edge[i].w; 48 edge[i].w=0; 49 return true; 50 } 51 } 52 return false; 53 } 54 void dfs(int u,int father) 55 { 56 for(int i=0;i<=t;++i) 57 dp[u][i]=c[u]; 58 for(int i=head[u];i!=-1;i=edge[i].next) 59 { 60 int v=edge[i].to; 61 int w=edge[i].w*2;//还要返回主干的结点,所以时间是两倍 62 if(v==father) 63 continue; 64 dfs(v,u); 65 for(int j=t;j>=w;--j) 66 { 67 for(int k=0;k<=j-w;++k) 68 { 69 dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][j-w-k]); 70 } 71 } 72 } 73 } 74 int main () 75 { 76 while(scanf("%d%d",&n,&t)!=EOF) 77 { 78 init(); 79 for(int i=1;i<n;++i) 80 { 81 int x,y,w; 82 scanf("%d%d%d",&x,&y,&w); 83 addedge(x,y,w); 84 } 85 for(int i=1;i<=n;++i) 86 scanf("%d",&c[i]); 87 sum=0; 88 short_path(1,-1); 89 t-=sum; 90 if(t<0) 91 printf("Human beings die in pursuit of wealth, and birds die in pursuit of food!\n"); 92 else 93 { 94 dfs(1,-1); 95 printf("%d\n",dp[1][t]); 96 } 97 98 } 99 }