题意:一个信号源,通过若干节点进行传输给若干居民。传输线路可以看成一棵树,信号源在根,居民是树叶。中间的非根非叶节点是转发点。每条传输线路都有一定的成本。每个居民为了看电视都出特定的钱。问在不亏损的情况下,电视公司能让居民看上电视的最大数量。
思路:树形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;
}