题目链接:http://poj.org/problem?id=1155
好长时间没写过树形DP了,突然间想写道题,就在网上随便搜了一题。
题目大意:
要转播一场球赛,根节点和所有中转节点都有一个信号塔可以传播信号,所有的叶子节点表示用户。
每条边都有一个权值,表示传播过来需要的花费。
当能传到用户的时候,用户会付一定的费用,问在不亏本的情况下,最多能让几个用户观看到这场球赛。
思路:
用一个DP[u][j]表示以u为根的子树,满足j个用户的时候最多能盈利多少。
那最后找一个最大的j满足DP[1][j]>=0 就是所求。
状态转移:
如果v为u的一个孩子节点。
那么DP[u][i]=Max(DP[u][i] , DP[v][j] + DP[u][i-j] - 它们相连的那条边的权值 )
i <= sum[u] ,sum[u]表示当前已枚举到的u下面叶子节点的个数。
j<=sum[v] ,sum[v]表示v下面叶子节点的个数
code:
View Code
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 3005 5 # define INF 0xfffffff 6 struct node{ 7 int from,to,next,val; 8 }edge[2*N]; 9 int head[N],tol,V[N],DP[N][N],sum[N]; 10 void add(int a,int b,int c) 11 { 12 edge[tol].from=a;edge[tol].to=b;edge[tol].val=c;edge[tol].next=head[a];head[a]=tol++; 13 } 14 int Max(int a,int b) 15 { 16 return a>b?a:b; 17 } 18 void dfs(int u,int father) 19 { 20 int i,j,k,v; 21 for(j=head[u];j!=-1;j=edge[j].next) 22 { 23 v=edge[j].to; 24 if(v==father) continue; 25 dfs(v,u); 26 sum[u]+=sum[v]; 27 for(i=sum[u];i>=1;i--) ///sum[u]表示已枚举到的u下面叶子节点的个数,一定要倒序枚举,不然会出现什么错误 (╯▽╰) 28 for(k=1;k<=sum[v] && k<=i;k++) ///sum[v]表示v下面叶子节点的个数 29 DP[u][i]=Max(DP[u][i],DP[v][k]+DP[u][i-k]-edge[j].val); 30 } 31 } 32 int main() 33 { 34 int i,j,n,m,num,ai,ci; 35 while(scanf("%d%d",&n,&m)!=EOF) 36 { 37 tol=0; 38 memset(head,-1,sizeof(head)); 39 for(i=1;i<=n-m;i++) 40 { 41 scanf("%d",&num); 42 while(num--) 43 { 44 scanf("%d%d",&ai,&ci); 45 add(i,ai,ci); 46 add(ai,i,ci); 47 } 48 } 49 for(i=n-m+1;i<=n;i++) 50 scanf("%d",&V[i]); 51 memset(sum,0,sizeof(sum)); 52 53 for(i=0;i<=n;i++) 54 { 55 DP[i][0]=0; 56 for(j=1;j<=n;j++) 57 DP[i][j]=-INF; 58 } 59 60 for(i=n-m+1;i<=n;i++) 61 { 62 sum[i]=1; 63 DP[i][1]=V[i]; 64 } 65 66 dfs(1,0); 67 68 for(i=m;i>=0;i--) 69 if(DP[1][i]>=0) break; 70 printf("%d\n",i); 71 } 72 return 0; 73 }