poj 1155 tele

来源:互联网 发布:淘宝神笔怎么保存模板 编辑:程序博客网 时间:2024/04/27 21:16

题目大意:

这道题的大意就是有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 0x7fffffffusing 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
原创粉丝点击