题意:一个信号源,通过若干节点进行传输给若干居民。传输线路可以看成一棵树,信号源在根,居民是树叶。中间的非根非叶节点是转发点。每条传输线路都有一定的成本。每个居民为了看电视都出特定的钱。问在不亏损的情况下,电视公司能让居民看上电视的最大数量。
思路:树形dp。dp[i][j]表示以i为根节点,通往j个树叶(居民)的最大收益。dp[i][j]=max(dp[i][j],dp[i][k]+dp[son][j-k]-w(i,son)),根据转移方程遍历son即可。其中leaf[i]数组表示以i为根的子树的树叶数量。
#include <stdio.h> #include <string.h> #define max(a,b) ((a)>(b)?(a):(b)) #define N 3005 #define INF 0x3fffffff struct edge{ int y,w,next; }e[N]; int n,u,first[N],leaf[N],dp[N][N],top; void init(){ int i,j; top = 0; memset(first,-1,sizeof(first)); memset(leaf,0,sizeof(leaf)); for(i = 1;i<N;i++) for(j = 1;j<N;j++) dp[i][j] = -INF; } void add(int x,int y,int w){ e[top].y = y; e[top].w = w; e[top].next = first[x]; first[x] = top++; } void dfs(int x){ int i,j,y,k,w; int temp[N]; for(i=first[x];i!=-1;i=e[i].next){ y = e[i].y; w = e[i].w; dfs(y); for(j = 0;j<=leaf[x];j++)//重要,因为下面求的时候dp[x][?]可能会改变,结果就会出错 temp[j] = dp[x][j]; for(j = 0;j<=leaf[x];j++) for(k = 1;k<=leaf[y];k++) dp[x][j+k]=max(dp[x][j+k],temp[j]+dp[y][k]-w); leaf[x] += leaf[y]; } } int main(){ freopen("a.txt","r",stdin); while(scanf("%d %d",&n,&u)!=EOF){ int i,j,num,w; init(); for(i = 1;i<=n-u;i++){ scanf("%d",&num); while(num--){ scanf("%d %d",&j,&w); add(i,j,w); } } for(i = n-u+1;i<=n;i++){ scanf("%d",&dp[i][1]); leaf[i] = 1; } dfs(1); for(i = u;i;i--) if(dp[1][i]>=0) break; printf("%d\n",i); } return 0; }