P1273 有线电视网

紧接着的一道树形dp。


这道题直接告诉你这是一颗树了。叶子节点是客户。

连边要费用,客户会付钱。求不亏本情况下能满足的最大用户。

还是老思路:用dp[i][j]表示第i个节点的子树下,取j个客户的最大值。

还是那个方程:dp[i][j] = max(dp[i][j], dp[i][j - k] + dp[v][k] - cost)

要不亏本,这个值肯定要大于0嘛。所以逆序搞一搞就好了。

代码:

#include<cstdio>
#include<algorithm>
const int maxn = 3005;
struct Edges
{
    int next, to, weight;
} e[maxn << 2];
int head[maxn], tot;
int n, m;
int dp[maxn][maxn];
int w[maxn];
void link(int u, int v, int w)
{
    e[++tot] = (Edges){head[u], v, w};
    head[u] = tot;
}
int dfs(int u)
{
    if(u > n - m)
    {
        dp[u][1] = w[u];
        return 1;
    }
    int ans = 1;
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].to;
        int t = dfs(v);
        ans += t;
        for(int j = ans; j >= 0; j--)
        {
            for(int k = 0; k <= t; k++)
            {
                if(j - k >= 0) dp[u][j] = std::max(dp[u][j], dp[u][j - k] + dp[v][k] - e[i].weight);
            }
        }
    }
    return ans;
}
int main()
{
    for(int i = 0; i < maxn; i++) for(int j = 0; j < maxn; j++) dp[i][j] = -19260817;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n - m; i++)
    {
        int k; scanf("%d", &k);
        while(k--)
        {
            int v, w; scanf("%d%d", &v, &w);
            link(i, v, w);
        }
    }
    for(int i = n - m + 1; i <= n; i++)
    {
        scanf("%d", &w[i]);
    }
    for(int i = 1; i <= n; i++) dp[i][0] = 0;
    dfs(1);
    for(int i = m; i >= 1; i--)
    {
        if(dp[1][i] >= 0)
        {
            printf("%d\n", i);
            break;
        }
    }
    return 0;
}
posted @ 2018-08-07 18:00  Garen-Wang  阅读(109)  评论(0编辑  收藏  举报