来源:http://poj.org/problem?id=1155
题意:一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值。现在要在电视台播放一场比赛,每个客户愿意花费cost[i]的钱观看,而从电视台到每个客户也都有个费用,并且经过一条边只会产生一个费用。问电视台不亏损的情况最多有几个客户可以看到比赛?
分析:解法为树形DP+背包,dp[i][j]表示以i为根节点选择j个用户产生的最大价值。状态转移方程: dp[s][1] =pay[s]; (s为叶子节点)
dp[s][k] = max(dp[s][k],dp[x][j] + dp[s][k-j] -v);(s为非叶子节点,j表示用户个数,i为容量,k为s的子节点,v为边权)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <cmath>
#define INF 0x3f3f
using namespace std;
struct edge
{
int x,y;
edge(int a,int b):x(a),y(b){};
};
vector<edge> data[3005];
int n,m,dp[3005][3005],pay[3005],num[3005];
void dfs(int s)
{
if(data[s].size()==0)
{
dp[s][1]=pay[s];
num[s]=1;
return ;
}
for(int i=0;i<data[s].size();i++)
dfs(data[s][i].x);
dp[s][0]=0;
int cur=0;
for(int i=0;i<data[s].size();i++)
{
int x=data[s][i].x,v=data[s][i].y;
cur+=num[x];
for(int k=cur;k>=0;k--)
{
for(int j=1;j<=num[x]&&j<=k;j++)
{
dp[s][k]=max(dp[s][k],dp[x][j]+dp[s][k-j]-v);
}
}
}
num[s]=cur;
}
int main()
{
scanf("%d%d",&n,&m);
memset(num,0,sizeof(num));
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=-1*INF;
for(int i=1;i<=n;i++)
data[i].clear();
for(int i=1;i<=n-m;i++)
{
int k,a,c;
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
scanf("%d%d",&a,&c);
data[i].push_back(edge(a,c));
}
}
for(int i=n-m+1;i<=n;i++)
scanf("%d",&pay[i]);
dfs(1);
int ans;
for(ans=n;ans>=0&&dp[1][ans]<0;ans--);
printf("%d\n",ans);
return 0;
}