poj1155

题意:给定一个树形图,节点数量3000。叶子节点是用户,每个用户如果能看上电视会交一定的电视费。看上电视的条件是从根到该用户的路径全部被修好,修每条边有一个费用。在不亏损(用户交钱总额>=修路总费用)的前提下,最多有多少人能看上电视。

分析:树形dp。dp[u][i][j]表示对于u节点,只看其前i个儿子对应的子树,在这些子树中总共让j个用户看上电视,这样的最大利润是多少(可以为负值)。

那么dp[u][i][j]=max(dp[u][i-1][j], dp[v][num[v][k] + dp[u][i - 1][j - k] - cost(u, v));其中v是u第i个儿子,num[v]表示以v为根的子树中总共有多少用户,cost(u,v)表示u到v这条边需要的花费。

以上是非叶子节点的转移方法,对于叶子节点则稍有不同,直接赋值为该用户交的钱即可。

#include <cstdio>
#include <vector>
using namespace std;

#define D(x) 

const int INF = 0x3f3f3f3f;
const int MAX_N = (int)(3e3) + 10;

int n, m;
int money[MAX_N];
vector<pair<int, int> > edge[MAX_N];
int dp[MAX_N][MAX_N];
int num[MAX_N];
int sum[MAX_N];

void input()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n - m; i++)
    {
        int k;
        scanf("%d", &k);
        for (int j = 0; j < k; j++)
        {
            int a, c;
            scanf("%d%d", &a, &c);
            edge[i].push_back(make_pair(a, c));
        }
    }
    for (int i = n - m + 1; i <= n; i++)
    {
        scanf("%d", &money[i]);
    }
}

void dfs(int u, int cost)
{
    if (u > n - m)
    {
        dp[u][0] = 0;
        num[u] = 1;
        sum[u] = money[u];
        dp[u][1] = money[u];
        return;
    }

    num[u] = 0;
    sum[u] = 0;
    for (int i = 0; i < (int)edge[u].size(); i++)
    {
        int v = edge[u][i].first;
        int w = edge[u][i].second;
        dfs(v, w);
        num[u] += num[v];
        sum[u] += sum[v];
    }
    fill_n(dp[u], num[u] + 1, -INF);
    dp[u][0] = 0;
    for (int i = 0; i < (int)edge[u].size(); i++)
    {
        int v = edge[u][i].first;
        int w = edge[u][i].second;
        for (int j = num[u]; j >= 1; j--)
        {
            int limit = min(j, num[v]);
            for (int k = 1; k <= limit; k++)
            {
                dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - k] - w);
            }
        }
    }
}

int main()
{
    input();
    dfs(1, 0);
    int ans = 0;
    for (int i = 0; i <= num[1]; i++)
    {
        if (dp[1][i] >= 0)
            ans = max(ans, i);
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= num[i]; j++)
        {
            D(printf("dp[%d][%d]=%d\t", i, j, dp[i][j]));
        }
        D(puts(""));
    }
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2015-04-05 20:31  金海峰  阅读(553)  评论(0编辑  收藏  举报