题目:点击打开链接
题意:
有一个电视台要用电视网络转播节目。这种电视网络是一树,树的节点为中转站或者用户。树节点的编号为1~N,其中1为总站,2~(N-M)为中转站,(总站和中转站统称为转发站)N-M+1~N为用户,电视节目从一个地方传到另一个地方都要费用,同时每一个用户愿意出相应的钱来付电视节目。现在的问题是,在电视台不亏本的前提下,要你求最多允许有多少个用户可以看到电视节目。
输入:
N M N表示转发站和用户总数,M为用户数
以下N-M行,第i行第一个K,表示转发站i和K个(转发站或用户)相连, 其后第j对数val,cost表示,第i个转发站到val有边,费用cost.
最后一行M个数表示每个用户愿意负的钱。
输出:
不亏本前提下,可以收到节目最多的用户数。
(如果某个用户要收到节目(叶子结点),那么电视台到该用户的路径节点的费用都要付)
思路:在树上进行背包,对于以u为根的子树,该子树供给给j个用户亏本的最少钱
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=3000+5;
const int INF=1000000000;
struct edge
{
int v,w,next;
}e[N];
int cnt,f[N][N],head[N],num[N];
int n,m;
void addedge(int u,int v,int w)
{
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
f[i][j]=-INF;
}
}
void dfs(int u)
{
if(u>=n-m+1)return;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].v;
dfs(v);
num[u]+=num[v];
for(int j=num[u];j>=0;j--){
for(int k=1;k<=num[v];k++){
if(j<k)break;
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-e[i].w);
}
}
// for(int j=num[u];j>=0;j--){
// for(int k=1;k<=num[v];k++){
// f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-e[i].w); //也有人这样写,其实都一样
// }
// }
}
}
int main()
{
// freopen("f.txt","r",stdin);
scanf("%d%d",&n,&m);
int s;
init();
int u,v,w;
for(int i=1;i<=n-m;i++){
scanf("%d",&s);
while(s--){
scanf("%d%d",&v,&w);
addedge(i,v,w);
}
}
for(int i=n-m+1;i<=n;i++){
scanf("%d",&f[i][1]);
num[i]=1;
}
dfs(1);
for(int i=m;i>=0;i--){
if(f[1][i]>=0){
printf("%d\n",i);
break;
}
}
return 0;
}