P1273 有线电视网

状态表示:
\(dp[u][j]\):以\(u\)为根的子树上有\(j\)个用户时的最大收益。计算结束后,使\(dp[1][i] \ge 0\)的最大\(i\)就是答案。

状态转移:

\[dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k] - w) \]

边界:
\(f[u][0]=0\)\(f[leaf][1]=pay[leaf]\)

注意点

枚举用户数和转移时需要小优化,而不能直接从总的用户数开始枚举,否则会超时。

const int N=3010;
vector<PII> g[N];
int f[N][N];
int pay[N];
int n,m;

int dfs(int u)
{
    f[u][0]=0;
    
    if(u > n-m)  // 叶子
    {
        f[u][1]=pay[u];
        return 1;
    }
    
    int sum=0;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].fi,w=g[u][i].se;
        int t=dfs(v);
        sum+=t;
        for(int j=sum;j>=0;j--)
            for(int k=0;k<=min(t,j);k++)
                f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-w);
    }
    
    return sum;
}

int main()
{
    memset(f,-0x3f,sizeof f);
    
    cin>>n>>m;

    for(int i=1;i<=n-m;i++)
    {
        int k;
        cin>>k;
        while(k--)
        {
            int a,c;
            cin>>a>>c;
            g[i].pb({a,c});
        }
    }

    for(int i=n-m+1;i<=n;i++) cin>>pay[i];

    dfs(1);

    for(int i=m;i>=1;i--)
        if(f[1][i] >= 0)
        {
            cout<<i<<endl;
            break;
        }
    //system("pause");
    return 0;
}
posted @ 2021-04-10 16:19  Dazzling!  阅读(40)  评论(0编辑  收藏  举报