P1273 有线电视网 - 树上背包
Posted zolrk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1273 有线电视网 - 树上背包相关的知识,希望对你有一定的参考价值。
树上背包看作分组背包就好了,收益临时变成负数也是可以的,并且收益的数值也很大,所以不再让收益当下标,放到数组里保存,设f[x][t]表示以x为根的子树中选择t个人观看节目,电视台的最大收益(让你求什么反而不一定要存在数组里面,可能是设为数组下标再判断可行性)
这题比较特殊,一般分组背包是过不了这么大数据的,但是因为实际有效的叶子节点十分少,所以可以优化(看注释)
另外注意f数组初始值的设定
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 3000 + 10;
const int INF = (1<<30) / 3; // 这里十分的坑。。。 一开始写成1<<30/3 先运算30/3,结果都变成-8了。。。
int n,m,f[MAXN][MAXN],last[MAXN],ans,edge_tot,vis[MAXN],son[MAXN];
struct Edge{
int u,v,w,to;
Edge(){}
Edge(int u, int v, int w, int to) :u(u), v(v), w(w), to(to) {}
}e[MAXN * 2];
inline void add(int u, int v, int w) {
e[++edge_tot] = Edge(u, v, w, last[u]);
last[u] = edge_tot;
}
void dfs(int x) {
vis[x] = 1;
if(n-m+1 <= x && x <= n) {
son[x] = 1;//这里。。。注意并不是son[x]传统子树大小了,而是包含多少用户节点
}
for(int i=last[x]; i; i=e[i].to) {
int v = e[i].v, w = e[i].w;
if(vis[v]) continue;
dfs(v);
son[x] += son[v];
for(int t=son[x]; t>=0; t--) {//因为son[x]比较小,所以这里优化确实是很大的
for(int j=0; j<=son[v]; j++) {
f[x][t] = max(f[x][t], f[x][t-j] + f[v][j] - w);//若有选0个的子树那么根本不用花费这w,所以选0个的不会有影响
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n-m; i++) {
int k,a,c;
scanf("%d", &k);
for(int j=1; j<=k; j++) {
scanf("%d%d", &a, &c);
add(i, a, c);
add(a, i, c);
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
f[i][j] = -INF;
}
// f[i][0] = 0; 这里不设为0是可以的,在上面的转移中若有选0个的子树出现一定不会影响已有的最优收益
}
for(int i=n-m+1; i<=n; i++) {
scanf("%d", &f[i][1]);
}
dfs(1);
for(int i=0; i<=m; i++) {
if(f[1][i] >= 0) {
ans = max(ans, i);
}
}
printf("%d
", ans);
return 0;
}
以上是关于P1273 有线电视网 - 树上背包的主要内容,如果未能解决你的问题,请参考以下文章