POJ1155--TELE ---树形DP

题意:给你一棵树,树的1号节点是电视台,2~n-m号节点是转播站,剩下的m个点是用户。点到点之间传递信号要消耗钱,最后一行给你能从每个用户收取的钱。然后问你在不亏本的情况下最多可以让多少的用户看到电视。

状态方程设为dp[i][j]表示,由点i开始传送,且让j名观众能够收到信号的情况下最大的利润

在搜索过程中,先把整棵树都跑完,处理出数组h[i] 表示以i为根节点的子树里有h[i]个观众。在递归到观众节点时,把该观众的收入存到dp[i][1]里。之后枚举i,j,k的值,分别表示走第几棵子树、递归到此最多能有几个观众、父亲节点下,该子树的前面的子树负责k个用户且这个子树负责j-k个用户。

如果j==k的话,表示不需要当前i这个子树来分担观众数,所以消耗是不需要加上父节点到该节点的那条花费的。最后遍历一遍,得到答案。

代码如下:

#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
#include<stdio.h>
using namespace std;
#define p pair<int,int>
const int inf=-2100000000;
vector<p> a[100050];
int dp[3010][3010],c[3010],h[3010];
int ans,mark; 
int solve(int u)
{
    if(a[u].size()==0)
    {
        dp[u][1]=c[u];
        h[u]=1;
        return 0;
    }

    for(int i=0;i<a[u].size();i++) 
    {
        solve(a[u][i].first);
    }
    int son=0;
    for(int i=0;i<a[u].size();i++)
    {
        for(int j=son+h[a[u][i].first];j>=1;j--)
        {
            for(int k=0;k<=son&&k<j;k++)
            {
                if(dp[u][k]!=inf&&dp[a[u][i].first][j-k]!=inf)
                {
                    if(k!=j) dp[u][j]=max(dp[u][j],dp[u][k]+dp[a[u][i].first][j-k]-a[u][i].second);
                    else dp[u][j]=max(dp[u][j],dp[u][k]+dp[a[u][i].first][j-k]);
                }
            }
        }
        son+=h[a[u][i].first];
    }    
    h[u]=son;

}
int main()
{
    int i,j,k,l,x,y,n,huan,m;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n-m;i++)
    {
        scanf("%d",&l);
        while(l--)
        {
            scanf("%d%d",&x,&y);
            a[i].push_back(p(x,y));
            //cout<<a[i][a[i].size()-1].first<<" "<<a[i][a[i].size()-1].second<<endl;
        }
        
    }for(i=n-m+1;i<=n;i++) scanf("%d",&c[i]);
    for(i=1;i<=n;i++)
    {
        dp[i][0]=0;
        for(j=1;j<=m;j++) dp[i][j]=inf;
    }
    solve(1);
    int mx=0;
    for(i=0;i<=m;i++) if(dp[1][i]>=0) mx=i;
    cout<<mx<<endl;
    
}

 

posted @ 2019-04-18 16:01  啾啾猫猫  阅读(155)  评论(0编辑  收藏  举报