POJ 1155 TELE(树形DP)

题意:

有一个电视台广播节目,广播的网络用一棵树表示,节点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;
}
posted @ 2013-02-20 17:13  kedebug  阅读(369)  评论(0编辑  收藏  举报