题目链接点击打开链接
题意:给定一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值。现在要在电视台播放一场比赛,每个客户愿意花费cost[i]的钱观看,而从电视台到每个客户也都有个费用,并且经过一条边只会产生一个费用。问电视台不亏损的情况最多有几个客户可以看到比赛?
我把边权放到相连的子节点当变成点权存负值,dp[i][j]表示i这个节点要j个叶子节点的最大收益
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=3e3+10;
int dp[maxn][maxn],val[maxn],tmp[maxn],cost[maxn];
vector<int>a[maxn];
int siz[maxn];
void dfs(int u,int fa)
{
if(a[u].size()==0)
{
siz[u]=1;
dp[u][1]=val[u];
}
dp[u][0]=0;
for(int i=0;i<a[u].size();i++)
{
int v=a[u][i];
if(v==fa) continue;
dfs(v,u);
memset(tmp,-0x3f,sizeof tmp);
for(int j=0;j<=siz[u];j++)
{
for(int k=0;k<=siz[v];k++)
{
int tm=cost[v];
if(k==0) tm=0;
tmp[j+k]=max(dp[u][j]+dp[v][k]+tm,tmp[j+k]);///转移方程1
}
}
siz[u]+=siz[v];
dp[u][0]=0;
for(int j=1;j<=siz[u];j++)
dp[u][j]=max(dp[u][j],tmp[j]);///转移方程2
}
}
int n,m;
int main()
{
///freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
memset(dp,-0x3f,sizeof dp);
memset(siz,0,sizeof siz);
for(int i=0;i<=n;i++) a[i].clear();
for(int i=1;i<=n-m;i++)
{
int x,k,y;
scanf("%d",&k);
while(k--)
{
scanf("%d%d",&x,&y);
cost[x]=-y;///转换成负点权
a[i].push_back(x);
}
}
for(int i=n-m+1;i<=n;i++)
scanf("%d",&val[i]);
dfs(1,-1);
for(int i=m;i>=0;i--)
if(dp[1][i]>=0)
{
printf("%d\n",i);
break;
}
}
return 0;
}