关闭

POJ 1155 TELE 树形dp

455人阅读 评论(0) 收藏 举报

http://poj.org/problem?id=1155

题意:某电台要广播一场比赛,该电台网络是由N个网点组成的一棵树,其中M个点为客户端,其余点为转发站。客户端i愿支付的钱为pay[i],每一条边需要的花费固定,问电台在保证不亏损的情况下,最多能使多少个客户端接收到信息?广播台所在的节点编号为1。

思路:树形dp。 用dp[root][d] 表示以root为根的子树中,保留d个用户的最大利润,由于每个结点的孩子的数目并不是最多只有两个,因此在求dp[root][d]的时候要进行一次横向的dp,即对root的所有孩子进行一个背包,在具体实现的时候我们可以一次求出root的所有可能d的最大利润(在背包dp的时候一起求出)。

需要注意的几点:1、树最好用邻接矩阵存储,这样可以节约寻找root孩子的时间;

     2、用一个num[ ]数组存储每个root状态数, 即d的最大值,这样比每次都从M开始求用时会少,避免超时。


代码:

#include<stdio.h>
#include<string.h>
#define INF 100000000
#define MAX(a,b) (a) > (b) ? (a) : (b)
int N ,M ;
int pay[3010] ;
int dp[3010][3010] ;
struct Node{
	int d ; 
	int next ;
	int n ;	
}edge[3010] ;
int root[3010] ;
int cnt ;
int num[3010] ;			//存放每个结点的状态种数 

void dfs(int u){
	if(u > N-M){
		num[u] = 1 ;
		dp[u][1] = pay[u] ;
		return ;	
	}	
	dp[u][0] = 0 ;
	for(int i=root[u]; i!=-1; i=edge[i].next){
		int v = edge[i].n ;
		int d = edge[i].d ;
		dfs(v) ;
		num[u] += num[v] ;
		for(int j=num[u];j>=0;j--){			//孩子的背包 
			for(int k=1;j-k>=0 && k<=num[v];k++){
				dp[u][j] = MAX(dp[u][j] , dp[u][j-k]+dp[v][k]-d );	
			}
		}
	}
}
void add(int u , int v, int d){
	edge[cnt].d = d ;
	edge[cnt].n = v ;
	edge[cnt].next = root[u] ;
	root[u] = cnt ++ ;	
}
int main(){
	int a , b ,c ,i, j ;
	while(scanf("%d %d",&N,&M) == 2){
		memset(root , -1  ,sizeof(root) );
		cnt = 0 ;
		for(i=1;i<=N-M;i++){
			scanf("%d",&a);	
			for(j=0;j<a;j++){
				scanf("%d %d",&b,&c);
				add(i,b,c);
			}
		}	
		for(i=N-M+1;i<=N;i++){
			scanf("%d",&pay[i]);				
		}
		for(int i=1;i<=N;i++){
			for(int j=0;j<=M;j++)
				dp[i][j] = -INF ;	
		}
		memset(num , 0 ,sizeof(num));
		dfs(1) ;
		for(int i=num[1];i>=0;i--){
			if(dp[1][i] >= 0){
				printf("%d\n",i);
				break ;	
			}	
		}      
	}	
	return 0 ;	
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:202996次
    • 积分:4162
    • 等级:
    • 排名:第7413名
    • 原创:212篇
    • 转载:17篇
    • 译文:0篇
    • 评论:42条
    最新评论