• 【LuoguP1273有线电视网】树形依赖背包


    参考论文http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html

    参考一篇写的很好的博文http://www.cnblogs.com/GXZC/archive/2013/01/13/2858649.html

    题目链接http://www.luogu.org/problem/show?pid=1273

    首先确定泛化物品的定义:价值随着体积变化的物体,如01背包中的f[i](体积为i时的最大价值最f[i])。

    泛化物品+普通物品:

    还是01背包,我们是怎么在泛化物品f[i]中加入一个体积为v,价值为w的普通物品的?

    f[i]=max(f[i],f[i-v]+w);

    O(n)解决。

    分析题目:可以看作是叶子节点的v=1,非叶子节点的v=0,w=父亲到该点的费用*(-1)+该点的money。就是一个树形依赖模型,每个节点是一个物品。

    设f[i][j]表示dfs序小于等于节点i的所有节点(物品)中,体积为j时的最大价值。

    设s为i的一个孩子。

    则f[i]管理的范围为红色圈,f[s]管理的范围为蓝色圈。

    从上往下(父亲-->孩子)dfs时,先让f[s]=f[i](s继承i的全部信息)

    强制让f[s]必须选择s,然后往下对s进行dp。

    让f[s]必须选普通物品s是因为选择了s才能选s的孩子:f[s]=f[i]; f[s][j]=max(f[s][j],f[s][j-v]+w);就是说,f[s]=f[i]+物品s

    如果不是选择孩子之前必须选择父亲:f[s]=max(f[i],f[i]+物品s)

    回溯(孩子-->父亲)时:将f[s]与f[i]合并。

    泛化物品的并: 因为两个泛化物品之间存在交集,所以不能同时两者都取,那么我们就需要求 泛化物品的并,对同一体积,我们需要选取两者中价值较大的一者,效率 O(C)。

    F[j] = max{ F1[j] , F2[j] } (C>=j>=0)

    我的代码以及注释:

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<algorithm>
     6 using namespace std;
     7 
     8 const int N=3010,Inf=(int)1e9;
     9 struct node{
    10     int x,y,d,next;
    11 }a[2*N];
    12 int len,n,m;
    13 int first[N],v[N],w[N],f[N][N],cost[N];
    14 
    15 int maxx(int x,int y){return x>y ? x:y;}
    16 
    17 void ins(int x,int y,int d)
    18 {
    19     len++;
    20     a[len].x=x;a[len].y=y;a[len].d=d;
    21     a[len].next=first[x];first[x]=len;
    22 }
    23 
    24 void init()
    25 {
    26     len=0;
    27     memset(first,0,sizeof(first));
    28     for(int i=1;i<=n-m;i++)
    29     {
    30         int k,x,d;
    31         scanf("%d",&k);
    32         for(int j=1;j<=k;j++)
    33         {
    34             scanf("%d%d",&x,&d);
    35             ins(i,x,d);ins(x,i,d);
    36         }
    37     }
    38     for(int i=n-m+1;i<=n;i++) scanf("%d",&w[i]);
    39     for(int i=1;i<=n-m;i++) v[i]=0;
    40     for(int i=n-m+1;i<=n;i++) v[i]=1;
    41 }
    42 
    43 void dfs(int x,int fa)
    44 {
    45     for(int i=first[x];i;i=a[i].next)
    46     {
    47         int y=a[i].y;
    48         if(y!=fa)
    49         {
    50             if(y>=n-m+1) cost[y]=-a[i].d+w[y];
    51             else cost[y]=-a[i].d;
    52             dfs(y,x);
    53         }
    54     }
    55 }
    56 
    57 void dp(int x,int fa)
    58 {
    59     for(int i=first[x];i;i=a[i].next)
    60     {
    61         int y=a[i].y,V=v[y],W=cost[y];
    62         if(y==fa) continue;
    63         
    64         for(int j=0;j<=n;j++) f[y][j]=f[x][j];
    65         dp(y,x);
    66         
    67         // 泛化物品(f[x])加普通物品(节点y:V=v[y],W=cost[y])
    68         // 初始化f[y]:f[y][j]=max(f[y][j],f[y][j-V]+W);
    69         
    70         // 泛化物品(f[x])与泛化物品(f[y])合并(f[x]与f[y]存在交集)
    71         // f[x][j]=maxx(f[x][j],f[y][j+V]);
    72         for(int j=V;j<=n-V;j++)
    73             f[x][j]=maxx(f[x][j],f[y][j-V]+W);//加普通物品y应放在dp(y)后,因为f[y]是表示y可选可不选的,如果先压进去就必须选。
    74     }
    75 }
    76 
    77 
    78 
    79 int main()
    80 {
    81     freopen("a.in","r",stdin);
    82     // freopen("a.out","w",stdout);    
    83     scanf("%d%d",&n,&m);
    84     init();
    85     dfs(1,0);
    86     for(int i=1;i<=n;i++) 
    87         for(int j=0;j<=n;j++) 
    88             if(j==0) f[i][j]=0;
    89             else f[i][j]=-Inf;
    90     dp(1,0);
    91     int ans=0;
    92     for(int i=n;i>=1;i--)
    93         if(f[1][i]>=0) {printf("%d
    ",i);return 0;}
    94     printf("0
    ");
    95     return 0;
    96 }
  • 相关阅读:
    页面的三大家族
    封装函数
    图片自动播放的案例
    动画封装
    长图滚动案例+点名册案例
    时钟案例
    伪随机数,随机数种子seed
    numpy.tolist( )函数
    countif( ) 函数判断当前单元格的身份证号码是否重复出现
    Excel技巧
  • 原文地址:https://www.cnblogs.com/KonjakJuruo/p/5783888.html
Copyright © 2020-2023  润新知