P1273 有线电视网

P1273 有线电视网

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

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

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

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

Solution

以前(两天前)以为树上背包就是个板, 没想到可以有变化的。。还是太弱了
之前一般设计的状态为 \(dp[u][j]\) 表示 \(u\) 节点选择 \(j\) 个用户可以获得的利益最大值
那么当转移到根节点时倒叙看看有哪个点利益大于等于 \(0\) 则就是最多能提供的用户啦
转移同一般的树上背包
本来觉得需要先处理出一个节点子树中的子节点数, 不然无法完全转移
后面才发现 是可以从从容量小的转移过去的啊
是可以从容量小的转移过去的啊 还造了了数据跑看能不能 \(hack\) 来着
嘛, 有点探究精神总归是好的啦

认真想清楚模型的意义,搞明白为什么可以这样,而不是简单的知道怎样做,就套上一个模板了事,那样,是不是也太糟蹋这门科学了

Code

预处理の原版

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 6019,INF = 1e9 + 19;
int head[maxn],nume = 1;
struct Node{
    int v,dis,nxt;
    }E[maxn << 3];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
int num, m, val[maxn];
int size[maxn];
int dp[maxn][maxn];//dp[u][j]代表子树中有j个用户时获利最大值
void dfs1(int u, int F){
	size[u] = 0;
	if(u >= num - m + 1){size[u] = 1;return ;}
	for(int i = head[u];i;i = E[i].nxt){
		int v = E[i].v;
		if(v == F)continue;
		dfs1(v, u);
		size[u] += size[v];
		}
	}
void dfs2(int u, int F){
	if(u >= num - m + 1){dp[u][1] = val[u];return ;}
	dp[u][0] = 0;
	for(int i = head[u];i;i = E[i].nxt){//枚举组
		int v = E[i].v;
		if(v == F)continue;
		dfs2(v, u);
		for(int j = size[u];j >= 0;j--){//枚举能接纳的用户数
			REP(k, 0, size[v]){//选一下吧
				if(j >= k)dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k] - E[i].dis);
				}
			}
		}
	}
int main(){
	num = RD(), m = RD();
	REP(i, 1, num - m){
		int k = RD();
		while(k--){
			int v = RD(), dis = RD();
			add(i, v, dis), add(v, i, dis);
			}
		}
	REP(i, num - m + 1, num)val[i] = RD();
	dfs1(1, -1);//预处理子树中子节点大小
	REP(i, 1, num)REP(j, 0, size[1])dp[i][j] = -1e9;
	dfs2(1, -1);
	printf("%d\n", dp[1][5]);
	}
posted @ 2018-10-18 20:32  Tony_Double_Sky  阅读(167)  评论(0编辑  收藏  举报