NKOI 期末赛题C 比赛转播

比赛转播

Time Limit:10000MS  Memory Limit:100000K
Total Submit:25 Accepted:18 
Case Time Limit:1000MS

Description

(tele.cpp/.in/.out 时限1s 内存128M) 
  一个电视网络计划转播一场足球比赛。网络中的传输点和接收点(即用户)可以表示一棵树。这棵树的根是一个传输点,它将转播比赛。树的叶节点是可能要观看这场比赛的用户(他当然可以选择不看比赛,这样就不用交款)。其他非根节点、非叶节点的中间节点为数据的中间传输点。将一个信号从一个传输点传到另一个传输点的花费是给定的。整个转播的费用就是每一个传输费用的总和。 
每一个用户(叶节点)都准备付一定的费用来观看这场比赛。电视网络公司要决定是否要给这个用户提供电视信号。例如:给一个叶节点传输信息的花费太大,而这个用户愿意的付款也很少时,电视公司可能选择不给他转播比赛。找到一个传输方案使最多的用户能看到转播比赛,且转播的费用不超过所有接收信号用户的交款。

Input

第一行包含两个整数NMN表示分别表示树的节点数,M表示想观看比赛的用户数(叶节点数)。树的根节点用1表示,中间节点的标号为2N-M,用户的节点标号为N-M+1N 
接下来的N-M行每行表示一个传输点的信息,第i行表示编号i-1的传输点的信息 
每行描述如下: 
K A1 C1 A2 C2 …… Ak Ck 
表示一个传输点将信号传给K个点,每一个包含两个数ACA表示传输点或用户的节点号,C表示传输的花费。 
最后一行有M个整数分别表示N-M+1N号用户看这场比赛愿意的付费。

Output

一行包含一个整数,最大的用户数

Sample Input

样例输入1

5 3

2 2 2 5 3   

2 3 2 4 3  

3 4 2        

样例输入2

5 3

2 2 2 5 3

2 3 2 4 3

4 4 2

样例输入3

9 6

3 2 2 3 2 9 3

2 4 2 5 2

3 6 2 7 2 8 2

4 3 3 3 1 1

Sample Output

样例输出1

2

样例输出2

3

样例输出3

5

Hint

2<=N<=3000,1<=M<=N-1 每次传输的费用和每个用户愿意提供的费用都<=100


树形背包问题 
状态:dp[u][j]表示为u根的子树中的j个叶子节点提供直播服务能获得的最多收益(可能为负)
状态转移:dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-w[u][v])
v为u的儿子节点,w[u][v]为u到v所需费用,  0<=j<=Cnt[u]  Cnt[u]表示u为根的子树中,叶子节点的总个数 

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=3005;
const int inf=1e9;
int n,p,w[maxn],cnt[maxn],start,ct,f[maxn][maxn];
int END[2*maxn],NEXT[2*maxn],LAST[maxn],LEN[2*maxn];
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
void insert(int a,int b,int l){  
    END[++ct]=b;  
    LEN[ct]=l;  
    NEXT[ct]=LAST[a];  
    LAST[a]=ct;  
}
void dp(int u,int fa){
	int i,j,k,q;
	for(i=LAST[u],j=END[i];i;i=NEXT[i],j=END[i]){
		dp(j,u);
		cnt[u]+=cnt[j];
		for(k=cnt[u];k>=1;k--)
	        for(q=1;q<=cnt[j];q++)
	            f[u][k]=max(f[u][k],f[u][k-q]+f[j][q]-LEN[i]);
	} 
}
int main(){
	_read(n);_read(p);
	int i,j,t,a,c;
	start=n-p+1;
	for(i=1;i<start;i++){
		_read(t);
		for(j=1;j<=t;j++){
			_read(a);_read(c);
			insert(i,a,c);
		}
	}
	for(i=1;i<=n;i++)
	    for(j=1;j<=p;j++)
	        f[i][j]=-inf;
	for(i=start;i<=n;i++){
		_read(f[i][1]);
		cnt[i]=1;
	}
	dp(1,0);
	for(i=p;i>=0;i--)
	    if(f[1][i]>=0){cout<<i;break;}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值