poj 1155 树形dp(不亏损让尽量多居民看上电视)

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

思路:树形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;
}


你可能感兴趣的:(poj 1155 树形dp(不亏损让尽量多居民看上电视))