• [bzoj2427] [HAOI2010] 软件安装(强连通分量)(树形背包DP)


    题干:
      现在我们的手头有N个软件,对于一个软件 i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即 Vi 的和最大)。但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件 i 依赖软件 j )。幸运的 是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一 次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    题解:
      先看一下题面,体积?空间?那肯定是背包啊。于是作者便高高兴兴地打出状态转移方程:

    dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+v[x]) (错解)

      于是喜提一0分。。。

      本题正解其实就是背包dp,又因为题干中说 一个软件最多依赖另外一个软件(每个节点最多只有一个出度) 那应该就是一个树上dp(作者表示连这是一棵树也要看不出来了)。

      于是作者又高高兴兴地打出状态转移方程(分组背包模型):

    dp[i][j]=ΣiL=0 ΣjR=0 max(dp[L][R])+v[x] (正解)

      于是又喜提一0分。。。

      考虑一下:每个节点最多只有一个出度,它就一定是一棵树吗?显然不是。这就需要我们用 tarjan 缩点来解决(合并权值与体积为一点,重新建图)。

      交上代码,又双是0分。。。

      最终其实错在了:在最终加上现节点的权值时(上面更新的是贡献最大子节点权值和),需要将体积小于 现节点的体积 的情况归零(我们上面更新的就是 给现节点的体积留有位置时的情况,针对不满足的情况就需归0)。

      还有一种打法:先加上现节点的权值,再进行比较,求最大值。当现在节点所枚举到的体积与子节点所枚举到的体积相等时,即未选现节点的权值时,需要特殊处理,防止更新出类似完全背包一样的重复的情况。

      有依赖的dp就和分组dp很像,我们可以把没有依赖的物品称主件,有依赖的称为附件,本题是在树上,那么一定要定义给以i为根的子树分配 vi 的空间所能达到的最大价值,那然后发现多个儿子转移到父亲似乎不好转移,父亲好像要给儿子分配时间。

      重新考虑一下,对于一个主件对应的几个附件可以看作是决策集合,那么这个集合在子树中有指数级多的的决策,怎么办呢?如果我们优化的话,对于每一个决策,我们要找相同体积找价值最大的,这是显然的,然后从子树转移过来的时候,我们会发现所谓的dp[i][v]就是 vi 体积下对应的那个最大的价值,决策已经保证最优,只要循环 vi 转移即可,道理同上。

    Code:

      1 #include<cstdio>
      2 #include<cstring>
      3 #define $ 110
      4 using namespace std;
      5 int n,m,v[$],w[$],d[$],dp[$][$*5],ans,dfn[$],low[$],tar,sta[$],up,circle=1,cir[$];
      6 int wc[$],vc[$],second[$],first[$],tot1,tot2,sum[$],out[$];
      7 bool judge[$];
      8 struct tree{    int to,next;    }a[$],b[$];
      9 inline int read(){
     10     char a=getchar();
     11     int sum=0;
     12     while(a<'0'||a>'9')   a=getchar();
     13     while(a>='0'&&a<='9') sum=(sum<<1)+(sum<<3)+a-'0',a=getchar();
     14     return sum;
     15 }
     16 inline int max(int x,int y){    return x>y?x:y;    }
     17 inline int min(int x,int y){    return x<y?x:y;    }
     18 inline void add(int x,int y){
     19     a[++tot1]=(tree){    y,first[x]    };
     20     first[x]=tot1;
     21 }
     22 inline void addf(int x,int y){
     23     b[++tot2]=(tree){    y,second[x]    };
     24     second[x]=tot2;
     25 }
     26 inline void tarjan(int x){
     27     dfn[x]=low[x]=++tar;
     28     sta[++up]=x;
     29     judge[x]=1;
     30     for(register int i=second[x];i;i=b[i].next){
     31         int to=b[i].to;
     32         if(!dfn[to]){    
     33             tarjan(to);
     34             low[x]=min(low[x],low[to]);
     35         }
     36         else if(judge[to]) low[x]=min(low[x],dfn[to]);
     37     }
     38     if(dfn[x]==low[x]){
     39         int tmp;  ++circle;
     40         do{
     41             tmp=sta[up--];
     42             judge[tmp]=0;
     43             cir[tmp]=circle;
     44             w[circle]+=wc[tmp];
     45             v[circle]+=vc[tmp];
     46         }while(tmp!=x);
     47     }
     48 }
     49 inline void dfs(int x){
     50     for(register int i=first[x];i;i=a[i].next){
     51         int to=a[i].to;
     52         dfs(to);
     53         for(register int j=m;j>=0;--j){
     54             for(register int k=j;k>=0;--k){
     55                 dp[x][j]=max(dp[x][j],dp[x][k]+dp[to][j-k]);
     56             }
     57         }
     58     }
     59     for(register int i=m;i>=w[x];--i)   dp[x][i]=dp[x][i-w[x]]+v[x];
     60     for(register int i=w[x]-1;i>=0;--i) dp[x][i]=0;
     61 }
     62 /*
     63 void DP(int x)
     64 {
     65     for(int i=bit[x];i<=m;i++)
     66         dp[x][i]=val[x];
     67     for(int i=fi[x];i!=0;i=e[i].ne)
     68     {
     69         int y=e[i].v;
     70         DP(y);
     71         for(int j=m;j>=bit[x];j--)
     72         {
     73             int temp=dp[x][j];
     74             for(int k=bit[x];k<=j-bit[y];k++)
     75             {
     76                 if(k!=j)
     77                     dp[x][j]=max(dp[x][j],dp[x][k]+dp[y][j-k]);
     78                 else
     79                     dp[x][j]=max(dp[x][j],temp+dp[y][j-k]);
     80             }
     81         }
     82     }
     83 }*/
     84 signed main(){
     85     scanf("%d%d",&n,&m);
     86     for(register int i=1;i<=n;++i) wc[i]=read();
     87     for(register int i=1;i<=n;++i) vc[i]=read();
     88     for(register int i=1,x;i<=n;++i) if(x=read()) addf(x,i);
     89     for(register int i=1;i<=n;++i)   if(!dfn[i])  tarjan(i);
     90     for(register int i=1;i<=n;++i)
     91         for(register int j=second[i];j;j=b[j].next){
     92             int to=b[j].to;
     93             if(cir[i]!=cir[to]) add(cir[i],cir[to]),out[cir[to]]++;
     94         }
     95     for(register int i=2;i<=circle;++i) if(!out[i]) add(1,i);
     96     dfs(1);
     97     for(register int i=0;i<=m;++i) ans=max(ans,dp[1][i]);
     98     printf("%d
    ",ans);
     99 }
    100             
    View Code
    越努力 越幸运
  • 相关阅读:
    PHP 生成随机字符串与唯一字符串
    大数据和AI怎么与现代教育相结合?
    大数据和AI怎么与现代教育相结合?
    python正常时间和unix时间戳相互转换的方法
    python正常时间和unix时间戳相互转换的方法
    人人都谈大数据,你考虑过小数据的感受吗
    人人都谈大数据,你考虑过小数据的感受吗
    浅析机器学习的主题模型和语义分析
    浅析机器学习的主题模型和语义分析
    大数据对当代企业推广价值何在
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11173849.html
Copyright © 2020-2023  润新知