洛谷P1273 有线电视网

题目简介

题目描述

某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

输入输出

输入文件的第一行包含两个用空格隔开的整数N和M,其中2≤N≤3000,1≤M≤N-1,N为整个有线电视网的结点总数,M为用户终端的数量。

第一个转播站即树的根结点编号为1,其他的转播站编号为2到N-M,用户终端编号为N-M+1到N。

接下来的N-M行每行表示—个转播站的数据,第i+1行表示第i个转播站的数据,其格式如下:

K A1 C1 A2 C2 … Ak Ck

K表示该转播站下接K个结点(转播站或用户),每个结点对应一对整数A与C,A表示结点编号,C表示从当前转播站传输信号到结点A的费用。最后一行依次表示所有用户为观看比赛而准备支付的钱数。

输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。$$

题目分析

思路分析

应该很简单能看出这是一个树上分组背包,考虑状态

设f[u][i]代表以u为节点,选i个用户所能得到的钱的最大值

转移方程:

对于节点v,分配其j个用户,则节点u只需要分配到i-j个用户,如此进行枚举

有:f[u][i]=max(f[u][i],f[[v][j]+f[u][i-j]-e(w))

代码

#include<bits/stdc++.h>
#define re register
#define ll long long
using namespace std;
inline int read()
{
	ll k=1,sum=0;
	char c=getchar();
	for(;c<'0' || c>'9';c=getchar()) if(c=='-') k=-1;
	for(;c>='0' && c<='9';c=getchar()) sum=sum*10+c-'0';
	return sum*k;
} 
const int N=3e3+10;
int n,m;
int val[N];
int dp[N][N];
int head[N],cnt;
struct Edge{
	int to,nxt,w;
}edge[N<<1]; 
inline void Add(int x,int y,int w){
	edge[++cnt].to=y;edge[cnt].nxt=head[x];edge[cnt].w=w;head[x]=cnt;
}
inline int dfs(int x){
	if(x>n-m){
		dp[x][1]=val[x];
		return 1;
	}int sum=0;
	for(re int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		int t=dfs(y);sum+=t;
		for(re int j=sum;j>0;--j){
			for(re int k=t;k;--k){
				if(j-k>=0) dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]-edge[i].w);
			}
		}
	}
	return sum;
}
int main()
{
	memset(dp,~0x3f,sizeof(dp));
	n=read();m=read();
	for(re int i=1;i<=n-m;++i){
		int Arknights=read();
		for(re int j=1;j<=Arknights;++j){
			int v=read(),w=read();
			Add(i,v,w);
		}
	}
	for(re int i=n-m+1;i<=n;++i) val[i]=read();
	for(re int i=1;i<=n;++i) dp[i][0]=0;
	dfs(1);
	for(re int i=m;i;--i){
		if(dp[1][i]>=0) {
			cout<<i;return 0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/IcedMoon/p/11428659.html