POJ 1155 ——TETE【树形背包】

题目传送门


Description

A TV-network plans to broadcast an important football match. Their network of transmitters and users can be represented as a tree. The root of the tree is a transmitter that emits the football match, the leaves of the tree are the potential users and other vertices in the tree are relays (transmitters).
The price of transmission of a signal from one transmitter to another or to the user is given. A price of the entire broadcast is the sum of prices of all individual signal transmissions.
Every user is ready to pay a certain amount of money to watch the match and the TV-network then decides whether or not to provide the user with the signal.
Write a program that will find the maximal number of users able to watch the match so that the TV-network’s doesn’t lose money from broadcasting the match.


Input

The first line of the input file contains two integers N and M, 2 <= N <= 3000, 1 <= M <= N-1, the number of vertices in the tree and the number of potential users.
The root of the tree is marked with the number 1, while other transmitters are numbered 2 to N-M and potential users are numbered N-M+1 to N.
The following N-M lines contain data about the transmitters in the following form:
K A1 C1 A2 C2 … AK CK
Means that a transmitter transmits the signal to K transmitters or users, every one of them described by the pair of numbers A and C, the transmitter or user’s number and the cost of transmitting the signal to them.
The last line contains the data about users, containing M integers representing respectively the price every one of them is willing to pay to watch the match.


Output

The first and the only line of the output file should contain the maximal number of users described in the above text.


Sample Input

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

5


Source

Croatia OI 2002 Final Exam - Second Day


题意

  • 电视台转播节目。对于每个根,其子结点可能是用户,也可能是中转站。但是用户肯定是叶子结点。传到中转站或是用户都要花钱,如果是用户,则还可以收钱。问在不亏本的前提下最多能有多少个用户看到节目。

题解

  • 树形背包DP的典型例题

  • 明确dp[i][j]含义,表示i节点,选j个用户,能得到的钱的最大值,然后对每个节点做分组背包。

  • 首先,背包的总容量相当于该点为根节点的子树中所有的用户数量(dp[i][j]的 j 不可能超过它连接的所有用户数)。然后,把该节点的每个儿子看成一组,每组中的元素为选一个,选两个…选n个用户。

    转移方程 dp[i][j] = max(dp[i][j],dp[i][j-k]+dp[v][k] - 这条边的花费) i,j不解释了,v表示枚举到这一组(即i的儿子),k表示枚举到这组中的元素:选k个用户。

    最后输出dp[1][i]>=0的i的最大值,所以反向枚举。

  • 简单来说就是把每个节点看成一个背包啦,它的容量就是以这个节点为根的子树大小,组数就是连接的儿子个数。

    每组都有很多选择,选一个,两个,三个用户,把这些选择当做组中的元素就好了,容易看出每组中只能选一个元素,比如你选择了选一个用户,就不可能同时选择选两个用户。


AC-Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <fstream>
#include <utility>
using namespace std;
typedef long long ll;
typedef pair<int, int> Pii;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int INF = 0x7fffffff;
const int MAXN = 3e3 + 5;
const int MOD = 1e9 + 7;

struct Edge {
	int next;
	int to;
	int w;
}edge[MAXN << 1];
int head[MAXN];
int cnt;
int dp[MAXN][MAXN];
int v[MAXN];//用户权值
int son[MAXN];
int n, m;
void addEdge(int u, int v, int w) {
	edge[cnt].w = w;
	edge[cnt].to = v;//edge[i]:第i条边的终点
	edge[cnt].next = head[u];//head[i]:以i为起点的最后一条边的储存位置
	head[u] = cnt++;
}
void dfs(int u, int fa) {
	dp[u][0] = 0;
	if (u > n - m) {//用户节点
		dp[u][1] = v[u];
		son[u] = 1;
		return;
	}
	for (int i = head[u]; ~i; i = edge[i].next) {
		int v = edge[i].to;
		if (v == fa)	continue;
		dfs(v, u);
		son[u] += son[v];
		for (int j = son[u]; j > 0; j--) {
			for (int k = 1; k <= son[v]; k++) {
				dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k] - edge[i].w);
			}
		}
	}
}
int main() {
	while (cin >> n >> m) {
		memset(dp, ~0x3f, sizeof dp);
		memset(head, -1, sizeof head);
		memset(son, 0, sizeof son);
		cnt = 0;
		for (int i = 1; i <= n - m; i++) {
			int k;
			cin >> k;
			for (int j = 0; j < k; j++) {
				int to, val;
				cin >> to >> val;
				addEdge(i, to, val);
			}
		}
		for (int i = n - m + 1; i <= n; i++) {
			cin >> v[i];
		}
		dfs(1, -1);
		for (int i = n; i >= 0; i--) {
			if (dp[1][i] >= 0) {
				cout << i << endl;
				break;
			}
		}
	}
	return 0;
}
发布了104 篇原创文章 · 获赞 60 · 访问量 5892

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/103115521