首页 > 其他分享 >【LuoGU 1273】有线电视网——树上分组背包问题

【LuoGU 1273】有线电视网——树上分组背包问题

时间:2023-07-23 15:56:56浏览次数:41  
标签:转播站 结点 sz int LuoGU 1273 用户 有线电视

有线电视网

题目描述

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

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

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

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

输入格式

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

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

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

\(K \ \ A_1 \ \ C_1 \ \ A_2 \ \ C_2 \ \ \ldots \ \ A_k \ \ C_k\)

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

输出格式

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

样例 #1

样例输入 #1

5 3
2 2 2 5 3
2 3 2 4 3
3 4 2

样例输出 #1

2

提示

样例解释

如图所示,共有五个结点。结点 ① 为根结点,即现场直播站,② 为一个中转站,③④⑤ 为用户端,共 \(M\) 个,编号从 \(N-M+1\) 到 \(N\),他们为观看比赛分别准备的钱数为 \(3\)、\(4\)、\(2\)。

从结点 ① 可以传送信号到结点 ②,费用为 \(2\);

也可以传送信号到结点 ⑤,费用为 \(3\)(第二行数据所示);

从结点 ② 可以传输信号到结点 ③,费用为\(2\);

也可传输信号到结点 ④,费用为 \(3\)(第三行数据所示)。

如果要让所有用户(③④⑤)都能看上比赛,则信号传输的总费用为:\(2+3+2+3=10\),大于用户愿意支付的总费用 \(3+4+2=9\),有线电视网就亏本了,而只让 ③④ 两个用户看比赛就不亏本了。

解法

注意到本题的特征:从若干个物品中选出一些来满足某一个限制条件,符合背包问题的特征,因此我们可以从背包问题出发思考。
题目中要选择的目标是树的叶子节点,由于叶节点被子树分成了若干个组,因此这个问题就是一个分组背包问题:
分组背包问题模板(滚动数组优化版本)

for(int i = 1; i <= n; i ++ ){    //组数
        for(int j = V; j >= 0; j -- ){     //背包容量
            for(int k = 0; k <= sz[i]; k ++ ){          //枚举每一组中的物品
                int vik = v[i][k], wik = w[i][k];
                if(j >= vik) f[j] = std::max(f[j], f[j - vik] + wik);
            }
        }
    }

设\(f[u][i][j]\)表示结点\(u\)的前\(i\)个儿子中选择\(j\)个结点的最大利润。我们考虑最后一个分界点:如果不取第\(i\)个结点,则状态转移为$$f[u][i][j] = f[u][i - 1][j]$$如果取第\(i\)个结点,记为\(v\),那么状态转移方程为$$f[u][i][j] = f[u][i - 1][j - k] + f[v][sz[v]][k] - w_{u, v}$$其中\(sz[v]\)表示\(v\)的子树大小,\(k\)表示从\(v\)的子树中选择的节点个数,\(w_{u, v}\)是\(u,v\)之间的花费.所以最终的状态转移方程为$$f[u][i][j] = max_{v \in son[u]}(f[u][i-1][j], f[u][i-1][j-k]+f[v][sz[v]][k] - w_{u,v})$$
显然根据\(0/1\)背包的思想我们可以对上式进行空间优化,只需要在枚举选择的节点个数时倒序枚举即可。优化后的状态转移方程为$$f[u][j] = max_{v \in son[u]}(f[u][j], f[u][j - k] + f[v][k] - w_{u, v})$$
注意本题最终问的问题,是要在不亏本的情况下能够观看转播的最多用户数,也就是满足\(f[1][j] \geq 0\)的最大的\(j\),因此在做完后需要从大到小枚举\(j\)来找到答案。

#include<bits/stdc++.h>

const int N = 3010;
const int inf = 0xcfcfcfcf;

int n, m;

std::vector<std::vector<std::pair<int, int>>> g(N);
std::vector<std::vector<int>> f(N, std::vector<int>(N, inf));
int sz[N], money[N];

void dfs(int u) {
	if(money[u]) {
		f[u][1] = money[u];
		sz[u] = 1;
		return;
	}

	for(auto edge: g[u]) {
		int v = edge.first, w = edge.second;
		dfs(v);
		sz[u] += sz[v];
		for(int j = m; j >= 1; j -- ) {
			for(int k = 0; k <= std::min(j, sz[v]); k ++ ) {
				f[u][j] = std::max(f[u][j], f[u][j - k] + f[v][k] - w);
			}
		}
	}
}

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	std::cin >> n >> m;

	for(int i = 1; i <= n - m; i ++ ) {
		int k;
		std::cin >> k;
		for(int j = 0; j < k; j ++ ) {
			int a, c;
			std::cin >> a >> c;
			g[i].push_back({a, c});
		}
	}

	for(int i = n - m + 1; i <= n; i ++ ) {
		std::cin >> money[i];
	}

	for(int i = 1; i <= n; i ++ ) f[i][0] = 0;

	dfs(1);
	int ans = 0;
	for(int i = m; i >= 1; i -- ) {
		if(f[1][i] >= 0) {
			ans = i;
			break;
		}
	}
	
	std::cout << ans;

	return 0;
}

标签:转播站,结点,sz,int,LuoGU,1273,用户,有线电视
From: https://www.cnblogs.com/yjx-7355608/p/17575097.html

相关文章

  • Luogu P4552 [Poetize6] IncDec Sequence 更好的题解
    原题链接第一步对于学过差分的人应该不难想定义差分数组$dis\quads.t.\quaddis[i]=a[i]-a[i-1]$那么不难发现问题一只要让\(dis[2]...dis[n]\)中全部为\(0\)即可区间\([l,r]\)加一操作在差分数组中意味着\(dis[l]=dis[l]+1,dis[r+1]=dis[r+1]-1\)即在差分数组......
  • Luogu 2791 幼儿园篮球题
    考虑枚举选出来\(i\)个没气的篮球,那么答案可以表示成:\[\text{ans}=\frac{1}{\dbinom{n}{k}}\sum\limits_{i=0}^{k}\dbinom{m}{i}\dbinom{n-m}{k-i}i^L\]注意到这里的组合数\(\dbinom{n}{m}\)在\(n<m\)或者\(m<0\)时无意义,直接当成\(0\)即可。考虑普通幂转下降幂:\[i^......
  • Luogu 6821 PA2012 Tanie linie
    这里只讲加强版,这是严格弱化。结论是贪心。每次取出最大和连续子段,目前答案加上这个子段和,然后再把这个子段取反(相反数T),然后求整个过程答案的最大值。考虑费用流模型。对于\(i\len\),\(S\toi\)连边,\(i\toT\)连边,流量为\(1\)费用为\(0\);\(i\toi+1\)连边,流量为\(1\)费......
  • Luogu 5296 生成树计数
    好像有道题是求生成树权值和的和的,考虑\(\sum\limits_{T}\sum\limits_{e\inE(T)}w_e\)咋做。每条边给一个边权\(v_e(x)=1+w_ex\),然后跑矩阵树:\[\text{ans}=[x]\sum\limits_{T}\prod\limits_{e\inE(T)}v_e(x)\]受到来自神秘力量的启示,我们考虑类似上面这样构造边权。考虑......
  • Luogu 3412 仓鼠找sugar II
    你也许说得对,但我是真看不懂第一篇题解那个答案式子……预处理是差不多的。设\(f_u\)表示从\(u\tofa(u)\)的期望步数,\(g_u\)为\(fa(u)\tou\)的期望步数,\(d_u\)为\(u\)的度数。那么显然有:\[f_u=\frac{1}{d_u}\left(1+\sum\limits_{v\inson(u)}(1+f_v+f_u)\right)......
  • 【题解】Luogu[P3360] 偷天换日
    solution开题显然是个树形dp,只不过在树形dp上又增加了背包问题。我们不妨将每个走廊看成一个点,把交叉口看成边(当然也可以把交叉口看成点,不过写起来麻烦一些),于是就转化为了一棵二叉树。我们设\(f_{i,j}\)表示以\(i\)为根的子树内,花费了不超过\(j\)时间,能拿到的最大价值......
  • 【题解】Luogu[P2607] [ZJOI2008] 骑士
    题目说给定\(n\)个点\(n\)个关系,也就是\(n\)条边,显然是基环树,又因为没有规定一定连通,于是我们可以将题目简化为给定一个基环树森林,点有点权,相邻的两个点不能同时选,问最大点权和。part1我们先考虑如果没有环,只是树,该怎么做。这一部分很简单,令\(f_{i,0/1}\)表示以\(i\)......
  • [刷题笔记] Luogu P1168 中位数
    ProblemDescription题目描述非常简洁,不作解释。Solution题目要求对前奇数项求中位数?朴素的做法是暴力,但是范围1e5显然T。这里主要介绍一种堆顶堆的做法。堆顶堆是什么呢?我们不妨开两个堆,一个大根堆一个小根堆。动态维护中位数,初始令前1位的中位数为\(a_i\),遍历数组,若遇到比中......
  • 洛谷 Luogu P1038 [NOIP2003 提高组] 神经网络
    这题看着很吓人实则很简单。求输出层,正着求很麻烦,因为知不道谁连向这个点,所以可以反向建边,反着求。拓扑+dfs,时间复杂度\(\text{O(n+m)}\)#include<iostream>#include<cstdio>#include<queue>#defineN105#defineM(N*N/2+114)structE{intv,w;......
  • luogu-modle
    P3383【模板】线性筛素数https://blog.csdn.net/huang_miao_xin/article/details/51331710首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;在6的倍数相邻两侧并不是一定就是质数。证明:令x≥1,将大于等于5的自然数可表示成:[6x-1......