有线电视网

传送门

仍然是一道标准的树形DP。

我们其实不必纠结到底剩余的钱数是多少,那并不重要,我们只关心其是否大于0。而且我们其实要求的是能连接的最大用户数。

用dp[i][j]表示在第i个节点连接了j个客户的最大利润。那么得到:

dp[i][j] = max(dp[i][j],dp[i][j-k] + dp[t][k] - e[i].v);

之后又可以做了……(好像这样的树形DP推出方程即结束)

注意初值要设成-INF(因为有负数)。还是因为背包的思想,我们在枚举第一维(当前点连接顾客数)的时候要倒着枚举。

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')

using namespace std;
typedef long long ll;
const int M = 10005;
const int INF = 1000000009;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

struct edge
{
    int next,to,v;
}e[10005];

int a[10005],n,m,dp[3005][3005],x,y,ecnt,head[10005];

void add(int x,int y,int z)
{
    e[++ecnt].to = y;
    e[ecnt].v = z;
    e[ecnt].next = head[x];
    head[x] = ecnt;
}

int dfs(int x,int fa)
{
    dp[x][0] = 0;
    if(x > n-m)
    {
        dp[x][1] = a[x];
        return 1;
    }
    int tot = 0;
    for(int i = head[x];i;i = e[i].next)
    {
        int t = e[i].to;
        if(t == fa) continue;
        int gg = dfs(t,x);
        tot += gg;
        per(j,tot,1)
        {
            rep(k,0,gg) dp[x][j] = max(dp[x][j],dp[t][k] + dp[x][j-k] - e[i].v);
        }
    }
    return tot;
}
int main()
{
    n = read(),m = read();
    rep(i,1,n)
    rep(j,1,n) dp[i][j] = -INF;
    rep(i,1,n-m)
    {
        int g = read();
        rep(j,1,g) x = read(),y = read(),add(i,x,y),add(x,i,y);
    }
    rep(i,n-m+1,n) a[i] = read();
    dfs(1,0);
    per(i,m,0) 
    {
        if(dp[1][i] >= 0)
        {
            printf("%d
",i);
            break;
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/captain1/p/9637056.html