P1273 有线电视网

//P1273 有线电视网
//花了些时间,弄明白了样例的输入输出
//如何转化为算法,还没有眉目
//http://blog.csdn.net/hehe_54321/article/details/75368187此文代码写得不错
//ans[i][j]表示第i个结点以下共j个用户观看时最大的赚钱量   
//(仍然没有想到)ans[u][i]=max{ans[u][i-j]+ans[v][j]-w}  
//具体解释:  
/*
ans[i][j][k]表示第i个结点以下前k个子结点中有j个用户观看时最大的赚钱量
ye[v]为v及以下叶节点数量
则对于边(u,v,w),ans[i][j][k]=max{ans[i][j-p][k-1]+ans[v][p][ye[v]]-w}(0<=p<=j,1<=j<=Σ(t属于前k个子结点的编号)ye[t])
省去k一维,则需要j从大到小循环:
ans[i][j]=max{ans[i][j-p]+ans[v][p]-w}(0<=p<=j,1<=j<=Σ(t属于前k个子结点)ye[t])
*/
/*
f(i, j),表示子树i转播给j个用户的最大收益值
这题可以看作是树上的分组背包,每个子树看作是一组物品,这一组物品可以取1个,2个...j个
然后就是套用分组背包的算法了
*/   
//程序有两个地方比较难理解:
//1、如何保证在取数过程中,叶结点无重复,如f[i][i-j]中的叶结点 f[v][j]中的叶结点不重复
//2、for(i=ye[u];i>0;i--)正确 for(i=1;i<=ye[u];i++)错误

//针对样例1,列出动态规划过程,能说明上述问题

//for(i=ye[u];i>0;i--)正确 过程如下:


f[1][1]=max(f[1][1],f[1][1]+f[5][0]-3)
f[1][1]=max(f[1][1],f[1][0]+f[5][1]-3)    1->5 取一个 最大值

f[2][1]=max(f[2][1],f[2][1]+f[4][0]-3)
f[2][1]=max(f[2][1],f[2][0]+f[4][1]-3)    2->4 取一个 最大值

f[2][2]=max(f[2][2],f[2][2]+f[3][0]-2)
f[2][2]=max(f[2][2],f[2][1]+f[3][1]-2)
f[2][2]=max(f[2][2],f[2][0]+f[3][2]-2)    2->4 2->3 取两个 最大值

f[2][1]=max(f[2][1],f[2][1]+f[3][0]-2)
f[2][1]=max(f[2][1],f[2][0]+f[3][1]-2)    2->4 2->3 取一个 最大值

f[1][3]=max(f[1][3],f[1][3]+f[2][0]-2)
f[1][3]=max(f[1][3],f[1][2]+f[2][1]-2)
f[1][3]=max(f[1][3],f[1][1]+f[2][2]-2)
f[1][3]=max(f[1][3],f[1][0]+f[2][3]-2)    1->5 1->4 1->3 取三个 最大值

f[1][2]=max(f[1][2],f[1][2]+f[2][0]-2)
f[1][2]=max(f[1][2],f[1][1]+f[2][1]-2)
f[1][2]=max(f[1][2],f[1][0]+f[2][2]-2)    1->5 1->4 1->3 取两个 最大值

f[1][1]=max(f[1][1],f[1][1]+f[2][0]-2)
f[1][1]=max(f[1][1],f[1][0]+f[2][1]-2)    1->5 1->4 1->3 取一个 最大值

//for(i=1;i<=ye[u];i++)错误 过程如下:


f[1][1]=max(f[1][1],f[1][1]+f[5][0]-3)
f[1][1]=max(f[1][1],f[1][0]+f[5][1]-3)    1->5 取一个 最大值

f[2][1]=max(f[2][1],f[2][1]+f[4][0]-3)
f[2][1]=max(f[2][1],f[2][0]+f[4][1]-3)    2->4 取一个 最大值

f[2][1]=max(f[2][1],f[2][1]+f[3][0]-2)
f[2][1]=max(f[2][1],f[2][0]+f[3][1]-2)    2->4 2->3 取一个 最大值

f[2][2]=max(f[2][2],f[2][2]+f[3][0]-2)
f[2][2]=max(f[2][2],f[2][1]+f[3][1]-2)
f[2][2]=max(f[2][2],f[2][0]+f[3][2]-2)    2->4 2->3 取两个 最大值

f[1][1]=max(f[1][1],f[1][1]+f[2][0]-2)
f[1][1]=max(f[1][1],f[1][0]+f[2][1]-2)    1->5 1->4 1->3 取一个 最大值

f[1][2]=max(f[1][2],f[1][2]+f[2][0]-2)
f[1][2]=max(f[1][2],f[1][1]+f[2][1]-2)    
//此处有问题 f[1][1]可能是1->4 故f[1][2]有可能取成 1->4 1->4 错误从此处开始
f[1][2]=max(f[1][2],f[1][0]+f[2][2]-2)    1->5 1->4 1->3 取两个 最大值

f[1][3]=max(f[1][3],f[1][3]+f[2][0]-2)
f[1][3]=max(f[1][3],f[1][2]+f[2][1]-2)
f[1][3]=max(f[1][3],f[1][1]+f[2][2]-2)
f[1][3]=max(f[1][3],f[1][0]+f[2][3]-2)    1->5 1->4 1->3 取三个 最大值    

//分析的过程中,同时将上述的两个顾虑问题解决。

//2017-9-1 17:55 AC
#include <stdio.h>
#include <string.h>
#define INF 999999
int head[3100],cnt=0,ye[3100],f[3100][3100];//f[i][j] i节点及以下共j个用户的最大收益 因收益有正有负,故初始化为绝对值比较大的负数
struct node{//邻接表
    int to,next,c;
}e[3100];//有向图
void addedge(int u,int v,int c){
    cnt++,e[cnt].to=v,e[cnt].c=c,e[cnt].next=head[u],head[u]=cnt;
}
int max(int a,int b){
    return a>b?a:b;
}
void dfs(int u){
    int v,b,i,j,c;//b边
    b=head[u];
    while(b){
        v=e[b].to,c=e[b].c;
        dfs(v);
        ye[u]+=ye[v];
        for(i=ye[u];i>=1;i--)
            for(j=0;j<=i;j++)
                f[u][i]=max(f[u][i],f[u][i-j]+f[v][j]-c);//1 此处写成 f[u][j]=max(f[i][j],f[u][i-j]+f[v][j]-c);
        b=e[b].next;
    }
}
int main(){
    int n,m,i,j,p,k,a,c,ans=-INF;
    memset(head,0,sizeof(head));
    memset(ye,0,sizeof(ye));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            f[i][j]=-INF;
    for(i=1;i<=n;i++)
        f[i][0]=0;//取0个,值为0
    p=n-m;
    for(i=1;i<=p;i++){
        scanf("%d",&k);
        for(j=1;j<=k;j++){
            scanf("%d%d",&a,&c);
            addedge(i,a,c);
        }
    }
    for(i=p+1;i<=n;i++){
        scanf("%d",&c);
        f[i][1]=c,ye[i]=1;
    }
    dfs(1);
    for(i=ye[1];i>=1;i--){
        ans=i;
        if(f[1][i]>=0)break;
    }
    printf("%d\n",ans);
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值