POJ1155 TELE

描述:

有一个由发射器和客户端构成的树形结构,1到n-m为发射器,n-m+1到n为客户端,各边有权值表示连接这条边邻接的两点所需的花费,客户端有点权值表示客户愿意付出的钱数。问在整体不亏损(总money大于等于总花费)的情况下,最多能服务到多少个客户端

分析:

这里实际上是树形DP里的分组背包问题。把各节点看成一组背包。dp[i][j]表示以节点i为根节点的子树中,有j个客户端得到服务时所能得到的最大利润(总money减去总花费)。于是有dp[u][1]=Money(u为客户端),dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-w)(u不为客户端,v为u的孩子节点,k为从孩子节点中选的客户数,w为u与v连边的权值)。

这样从下往上dfs即可。需要注意的是初始化问题,一般对于背包问题,求最大值时,对于无意义的情况(比如选取客户端数j大于i为根节点的子树内最大客户端数(即为sum[i])),这时就将其设为极小值(比如-INF)表示必定不会被选中,状态转移时也要注意一下。对于无意义的情况不作处理(即:if(dp[u][j-k]==-INF||dp[v][k]==-INF) continue;)。这样一开始就要把dp[i][0]=0,dp[i][1...n]=-INF。

代码:

#include<cstdio>
#include<cstring>
using namespace std;
#define N 3005
#define INF (1<<30)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x<y?y:x)
int dp[N][N],n,m,sum[N],cnt;
struct Edge{
    int to,w,next;
};
Edge edge[N<<1];int pedge[N];
void addedge(int u,int v,int w){
    edge[cnt].to=v;edge[cnt].w=w;
    edge[cnt].next=pedge[u];pedge[u]=cnt++;
}
void dfs(int u,int fa){
    int v,len;sum[u]=0;
    for(int i=pedge[u];i!=-1;i=edge[i].next){
        if((v=edge[i].to)==fa) continue;dfs(v,u);sum[u]+=sum[v];
        for(int j=sum[u];j>0;--j){
            len=min(j,sum[v]);
            for(int k=1;k<=len;++k){
                if(dp[u][j-k]==-INF||dp[v][k]==-INF) continue;
                dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-edge[i].w);
            }
        }
    }
    if(u>n-m) ++sum[u];
}
int main(){
    int a,c,t,ans;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(pedge,-1,sizeof(pedge));cnt=0;
        for(int i=1;i<=n;++i){
            dp[i][0]=0;
            for(int j=1;j<=n;++j) dp[i][j]=-INF;
        }
        for(int i=1;i<=n-m;++i){
            scanf("%d",&t);
            while(t--){
                scanf("%d%d",&a,&c);
                addedge(i,a,c);addedge(a,i,c);
            }
        }
        for(int i=n-m+1;i<=n;++i){
            scanf("%d",&t);dp[i][1]=t;
        }
        dfs(1,0);
        for(int i=sum[1];i>0;--i){
            if(dp[1][i]>=0) {ans=i;break;}
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值