poj 1155 tele

题目大意:

这道题的大意就是有N个节点,1号节点为发射站,2-n-m 是中途的传递站,n-m+1到n为用户,用户收到电视信号就会付出一定的钱,其中每条边有边权,代表传递的花费,

问在不亏本的情况下最多能给多少个用户发送信号。

题目分析:

这道题就是在树上背包,用Dp[i][j]表示以i为根节点管理j个用户所得到的最大收益。

接下来处理细节,怎么知道管理的用户的个数,用一个num数组标记,用户设为1,传递站设为0,在dfs求解子树的过程中进行更新,num[root]=∑num[son];

关于用户付出的金钱,直接用dp[i][0]表示,方便dp的时候使用;

那么dp方程就是:

dp[root][j+k]=max(dp[root][j+k],dp[root][j]+dp[son][k]);这就是一个背包;

我们采用正推的方式更新。

但是我们需要思考一个问题,在dp更新的时候可能会出现一些错误,例如在更新的过程中dp[root][j]可能是已经使用过这棵son子树了,而我们的j是在除当前son子树以外的用户数,所以我们需要用一个临时数组tem提取出来。

最后输出最大的i使得dp[1][i]大于0;

如下代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 3100
#define INF 0x7fffffff
using namespace std;
int n,m;
int dp[N][N],head[N],a,b,num[N],len,k,tem[N];
struct Node
{
    int now,next,w;    
}edge[N*2];
void dfs(int root,int fa)
{
    int p;
    for(int i=head[root];i!=-1;i=edge[i].next)
    {
        p=edge[i].now;
        if(p==fa) continue;
        dfs(p,root);
        for(int j=0;j<=num[root];j++)
            tem[j]=dp[root][j];
        for(int j=0;j<=num[root];j++)
            for(int k=1;k<=num[p];k++)
            {
                dp[root][j+k]=max(dp[root][j+k],tem[j]+dp[p][k]-edge[i].w);    
            }
        num[root]+=num[p];
    }    
    
}
void addedge(int u,int v,int W)
{
    edge[len].now=v;
    edge[len].next=head[u];
    edge[len].w=W;
    head[u]=len++;    
}
int main()
{
    scanf("%d%d",&n,&m);
    len=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-m;i++)
    {
        num[i]=0;
        scanf("%d",&k);
        for(int j=1;j<=k;j++)
        {
            scanf("%d%d",&a,&b);
            addedge(i,a,b);    
        }
    }
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
       dp[i][j]=-INF;
    for(int i=n-m+1;i<=n;i++)
    {
       num[i]=1;
       scanf("%d",&dp[i][1]);
    }
    dfs(1,-1);
    for(int i=m;i>=0;i--)
    {
        if(dp[1][i]>=0)
        {
            printf("%d\n",i);
            break;    
        }
    }
    //while(1);
    return 0;
     
    
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值