有线电视网
题目描述
某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。
从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。
现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。
写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。
输入格式
输入文件的第一行包含两个用空格隔开的整数
第一个转播站即树的根结点编号为
接下来的
输出格式
输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。
样例 #1
样例输入 #1
5 3
2 2 2 5 3
2 3 2 4 3
3 4 2
样例输出 #1
2
提示
样例解释
如图所示,共有五个结点。结点 ① 为根结点,即现场直播站,② 为一个中转站,③④⑤ 为用户端,共
从结点 ① 可以传送信号到结点 ②,费用为
也可以传送信号到结点 ⑤,费用为
从结点 ② 可以传输信号到结点 ③,费用为
也可传输信号到结点 ④,费用为
如果要让所有用户(③④⑤)都能看上比赛,则信号传输的总费用为:
题目分析
设状态转移方程dp[i][j]表示以i为根,且当前选择满足j个用户的最大获得费用。
则有dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[t][k]-w[i]);
其中t表示i的某个子树,k表示其中一颗子树t选择满足的用户数,w[i]表示结点i和结点t所连接的边的权值
代码实现
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3005,M=6005;
int h[N],e[M],ne[M],w[M],idx;
//sum[i]表示以i为根节点的子树中叶子结点的个数
//dp[i][j]表示以i为根,且当前选择满足j个用户的最大获得费用。
int dp[N][N],sum[N],n,m;
void add(int x,int y,int z){
e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;
}
void dfs(int u){
//如果为叶结点,则叶子结点个数为1
if(u>n-m){
sum[u]=1;
return;
}
for(int i=h[u];~i;i=ne[i]){
int t=e[i];
dfs(t);
//统计以u为根节点的子树中叶子结点的个数
sum[u]+=sum[t];
for(int j=sum[u];j>=0;j--){
for(int k=0;k<=j&&k<=sum[t];k++){
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[t][k]-w[i]);
}
}
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=n-m;i++){
int k;
cin>>k;
while(k--){
int y,z;
cin>>y>>z;
add(i,y,z);
}
}
//初始化
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) dp[i][j] = -1e9;
//叶子结点只能选择自己一个结点,获得费用即为叶结点本身价值
for(int i=n-m+1;i<=n;i++)cin>>dp[i][1];
dfs(1);
//从大到小遍历结点1选择满足的用户数,如果费用>=0(即不亏本),则输出能够满足的最大用户数
for(int i=sum[1];i>=0;i--){
if(dp[1][i]>=0){
cout<<i<<endl;
break;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)