[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
越努力 越幸运
原文地址:https://www.cnblogs.com/OI-zzyy/p/11173849.html