题意:
有一个电视台广播节目,广播的网络用一棵树表示,节点1表示电台,叶子结点表示用户,用户愿意付一定的钱去收看这个节目,
从非叶子结点到其他结点需要一定的费用(即从中继点到另一个中继点需要一些钱),问最后在不亏本的情况下,最多能使多少人收看到节目。
思路:
1. 和 POJ 1947 类似的题目,dp[u][i] 表示 u 节点为根,保留 i 个节点的最大盈利。
2. dp[u][i] = max(dp[u][i], dp[u][i-j] + dp[v][j] - w)
3. 如果叶子节点选定,则从叶子到根的一条路径上面的节点都应该被选定,所以初始化的时候赋值为 -INFS,只有当dp[叶子][i] 合法时,dp[rt][]才能合法。
4. sum[u] 表示以 u 为根节点的子树能覆盖多少个有效用户。算是解题中的一点优化。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 3010;
const int INFS = 0x3fffffff;
structedge {
int v, c;
edge* next;
} *V[MAXN], ES[MAXN * 3];
int EC, N, M, sum[MAXN], dp[MAXN][MAXN];
void addedge(int u, int v, int c)
{
ES[++EC].next = V[u];
V[u] = ES + EC;
V[u]->v = v, V[u]->c = c;
}
void initdata()
{
for (int i = 1; i <= N; ++i)
{
dp[i][0] = 0;
for (int j = 1; j <= M; ++j)
dp[i][j] = -INFS;
}
EC = 0;
memset(V, 0, sizeof(V));
for (int i = 1; i <= N - M; ++i)
{
int k, a, b;
scanf("%d", &k);
for (int j = 0; j < k; ++j)
{
scanf("%d %d", &a, &b);
addedge(i, a, b);
addedge(a, i, b);
}
}
memset(sum, 0, sizeof(sum));
for (int i = N - M + 1; i <= N; ++i)
scanf("%d", &dp[i][1]), sum[i] = 1;
}
void treedp(int u, int f)
{
for (edge* e = V[u]; e; e = e->next)
{
if (e->v == f)
continue;
treedp(e->v, u);
sum[u] += sum[e->v];
for (int i = sum[u]; i >= 1; --i)
for (int j = 1; j <= i; ++j)
if (dp[u][i-j] != -INFS && dp[e->v][j] != -INFS)
dp[u][i] = max(dp[u][i], dp[u][i-j] + dp[e->v][j] - e->c);
}
}
int main()
{
while (scanf("%d %d", &N, &M) != EOF)
{
initdata();
treedp(1, 0);
int i;
for (i = M; i >= 0; --i)
if (dp[1][i] >= 0)
break ;
printf("%d\n", i);
}
return 0;
}