题目大意:给定一棵有n个结点的树,其中1为发射台,m个中转站,剩下的是客户端,每条边都有权值,并且多次经过只会花费一次,并且客户端有收入val[i],问在使得电视台不亏损的情况下,最多能让几个客户端收到信号;
题目解析:定义dp[i][j]表示在以i为根节点的子树当中有j个客户端适用的最大收入,那么这就是个树上的背包问题了;
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int inf=0x3fffffff;
const int maxn=3010;
int tot,head[maxn],n,m,dp[maxn][maxn],val[maxn],sum[maxn];
struct Edge
{
int to,next,cost;
}edge[maxn<<1];
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void addedge(int u,int v,int w)
{
edge[tot].to=v;
edge[tot].cost=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int root,int fa)
{
dp[root][0]=0;
int num=0;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(fa==v) continue;
dfs(v,root);
num++;
sum[root]+=sum[v];
}
if(num==0)
{
dp[root][1]=val[root];
sum[root]=1;
}
else
{
for(int i=head[root];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
int t=edge[i].cost;
for(int j=sum[root];j>=1;j--)
{
for(int k=0;k<=j;k++)
{
if(dp[root][j-k]!=-inf&&dp[v][k]!=-inf)
{
dp[root][j]=max(dp[root][j],dp[root][j-k]+dp[v][k]-t);
}
}
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=-inf;
memset(sum,0,sizeof(sum));
for(int i=1;i<=n-m;i++)
{
int cnt;
scanf("%d",&cnt);
while(cnt--)
{
int u,cost;
scanf("%d%d",&u,&cost);
addedge(i,u,cost);
addedge(u,i,cost);
}
}
for(int i=n-m+1;i<=n;i++)
scanf("%d",&val[i]);
dfs(1,-1);
int ans=-inf;
for(int i=n;i>=0;i--)
{
//cout<<dp[1][i]<<endl;
if(dp[1][i]>=0)
{
printf("%d\n",i);
break;
}
}
}
return 0;
}