zoukankan      html  css  js  c++  java
  • 背包

    适合有背包基础的人食用,一些经典的背包模型,做的比较少,后面再更新吧

    一.需要注意的经典模板:
         详见《背包九讲》;

        划重点!

         1 . 树形依赖背包:

         有 直接dfs 和 转dfs序 两种实现方法,我比较习惯后面一种(蒟蒻不会第一种。。),写起来简洁一点:将原树转成dfs序    dfn[i]存第dfs序为i的节点编号,记录每个点的子树大小sz[],再从dfs序后往前做背包,考虑选和不选当前点,选的先强制塞进去,从f[i+1]转移,否则从f[i+sz[dfn[i]]]转移;

          2 . 多重背包的单调队列优化:

          画柿子:f[j]=max{f[j] , f[j-k*c[i]] + k*w[i]};

                       令j=a*c[i]+b;

                       f[j]=max{f[j] , f[(a-k)*c[i] + b] + k*w[i]};

                       f[j]=max{f[j] , f[(a-k)*c[i] + b] - (a-k)*w[i] + a*w[i]}

                       f[j]=max{f[j] , f[(a-k)*c[i] + b] – (a-k)*w[i]}+a*w[i];

                       对于一个确定的j,a*w[i]是确定的 , 取值只和(a-k)有关;

                      枚举里面的b,所有的j的b一样的话且a的系数递增即连续,就是a+b,2a+b,3a+b … k[ji]a+b,用一个单调队列维护 f[k[j1]*a+b]-k[j1]*w[i],转移j2的时候再加上k[j2]*w[i](j1<=j2),再塞进去,顺便记录下k,k[j2]-k[j1]大于物品个数就出队;

          通俗的理解是,对于余数相同的排排站,后面的一定可以从前面添几个物品,用一个差的思想。。。。。。。。(已词穷。。。)

    二.一些题:

    bzoj2287 消失之物                     (统计方案背包的删除)

       题目大意:有n个物品,fi,j表示删掉第i个物品剩下的n-1个物品可以组成体积j的方案数,求所有的fi,j并mod10输出一个矩阵;

        第一眼感觉是分治。。。

        先求出n个物品的fj数组(组成j体积的方案数),考虑删去一个物品的f数组如何变化,我们加入一个物品时更新方案是倒着枚举f[j]+=f[j-c[i]];现在假设设 1- i-1 都已经被还原那么 f[j] –= f[j-c[i]] 即可,所以顺着枚举并按照上述方式还原;

        我觉得,统计最大价值的应该不能删除;

        可以看下代码;

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=300001;
     5 int n,m,f[N],tmp[N],a[N];
     6 int main()
     7 {    freopen("bzoj2287.in","r",stdin);
     8     freopen("bzoj2287.out","w",stdout);
     9     scanf("%d%d",&n,&m);
    10     f[0]=1;
    11     for(int i=1,x;i<=n;i++){
    12         scanf("%d",&x);a[i]=x;
    13         for(int j=m;j>=x;j--)f[j]=(f[j]+f[j-x])%10;
    14     }
    15     for(int i=1;i<=n;i++){
    16         memcpy(tmp,f,sizeof(f));
    17         for(int j=a[i];j<=m;j++)tmp[j]=(tmp[j]-tmp[j-a[i]]+10)%10;
    18         for(int j=1;j<=m;j++)putchar(tmp[j]+'0');
    19         puts("");
    20     }
    21     return 0;
    22 }//by tkys_Austin;
    View Code

    poj3093  Margaritas on the River Walk (枚举+带强制的背包)

        题意:多组输入,每组给定物品数和背包容量以及接下来每个物品的体积,问有多少种方案,使得装入一些物品后,无法装入剩下的任意一个物品。

         无法装入的意思是剩下的物品任何一个都比现在剩下的体积小,可以枚举剩下的体积最大物品,不放入背包,比这个物品体积小的一定被放进了背包,比这个体积大的可以放也可以不放,从小到大排序,从前往后扫一遍,把1 - i-1体积加起来看成一个物品强制加进去,从后往前扫一遍i+1 - n做背包和1 - i-1这整个物品合并,统计比当前体积小的f答案;

        应该是可以处理体积相同的情况的,比如样例;

     

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=3010;
     6 typedef long long ll;
     7 int T,n,m,a[N];
     8 ll sum[N],f[N];
     9 int main()
    10 {    freopen("poj3093.in","r",stdin);
    11     freopen("poj3093.out","w",stdout);
    12     scanf("%d",&T);int Case=0;
    13     while(T--){
    14         scanf("%d%d",&n,&m);
    15         for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=0;
    16         sort(a+1,a+n+1);
    17         for(int i=1;i<=n;i++)sum[i]=a[i]+sum[i-1];
    18         for(int i=1;i<=m;i++)f[i]=0;
    19         f[0]=1;
    20         ll ans=0;
    21         for(int i=n;i;i--){
    22             for(int j=m;j;j--){
    23                 if(j+a[i]>m&&j>=sum[i-1])ans+=f[j-sum[i-1]];
    24                 if(j>=a[i])f[j]+=f[j-a[i]];
    25             }
    26         }
    27         printf("%d %lld
    ",++Case,ans);
    28     }
    29     return 0;
    30 }//by tkys_Austin;
    View Code


    bzoj2794 Cloakroom                    (二维限制的背包,离线做法)

         题目大意:n个物品属性a[i],b[i],c[i](a[i]<b[i]),q个询问m,k,s询问是否能选出一些物品使得a[i]<=m,b[i]>m+s,$sum{c[i]=k}$  ;

     

         每次都跑一遍肯定超时;
         k自然是背包的体积了,想想a,b;
         先撕烤a,b中的一个,假设b,发现普通可行性的背包里面存的只有01,十分浪费,让里面存构成这个体积的 每个方案 物品的最小b 的最大值,每次直接将m+s和f[k]比较就可以了;
         再撕烤加入a,将物品按照a排序,询问也离线了按照a排序 ,i,j两个指针指一指,枚举i(询问),将j(物品)移到最后一个a[j]<=q[i].m的物品,边移边加入f数组
        
     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int N=1000010,inf=0x3f3f3f3f;
     7 int n,m,f[N],ans[N],mxk;
     8 struct data{
     9     int id,x,y,c;
    10     bool operator <(const data&A)const{return x<A.x;}
    11 }Q[N],A[N];
    12 int main()
    13 {    freopen("bzoj2794.in","r",stdin);
    14     freopen("bzoj2794.out","w",stdout);
    15     scanf("%d",&n);
    16     for(int i=1,x,y,z;i<=n;i++){
    17         scanf("%d%d%d",&z,&x,&y);
    18         A[i]=(data){i,x,y,z};
    19     }
    20     scanf("%d",&m);
    21     for(int i=1,x,y,z;i<=m;i++){
    22         scanf("%d%d%d",&x,&y,&z);
    23         Q[i]=(data){i,x,x+z,y};
    24         mxk=max(y,mxk);
    25     }
    26     sort(A+1,A+n+1);
    27     sort(Q+1,Q+m+1);
    28     f[0]=inf;
    29     for(int i=1,j=1;i<=m;i++){
    30         while(j<=n&&A[j].x<=Q[i].x){
    31             for(int I=mxk;I>=A[j].c;I--)f[I]=max(f[I],min(A[j].y,f[I-A[j].c]));
    32             j++;
    33         }
    34         if(Q[i].y<f[Q[i].c])ans[Q[i].id]=1;else ans[Q[i].id]=0;
    35     }
    36     for(int i=1;i<=m;i++)ans[i]?puts("TAK"):puts("NIE");
    37     return 0;
    38 }//by tkys_Austin;
    View Code


    bzoj1190 梦幻岛宝珠          (二进制分层背包)

        题目大意:n<=100个物品,求体积不超过w<=2^30的最大价值,每个物品的体积可以写成a*(2^b)的形式且b<=30,a<=10;

         主要是w太大,但是a*(2^b)的形式很好,并且b的值有限,同时$ sum{a} $也很有限最大是1000,暂且随网上dalao的叫法叫做二进制分层背包吧;

         将输入的体积按b分组,对每一组内做一个普通的背包;

         然后f[i][j]表示j*2^i + w&((2^i)-1) 的体积的最大价值,或者说做到第0 – i-1位体积都满足w的限制,第i位层上体积为j的最大价值;

         先将f[i][j]初始化为每层单独的背包;

        层与层之间 f[i][j]=min{ f[i][k]+f[i-1][min( (k<<1) | ((w>>(i-1))&1 ) , 1000)] };

        由于此题只要求<=w所以可以对最大的j取min;

        但如果只能等于w的话大于直接取成inf,同时层内背包f[0]=0,其他全是inf(之前一直没想清楚这点);

        代码中的w[]是用来递推每一位的j上界,替换了1000;

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 using namespace std;
     5 const int N=110,M=1010;
     6 typedef long long ll;
     7 int n,m,p[31][N],q[31][N],cnt[31],w[31];
     8 ll f[31][M];
     9 int main()
    10 {    freopen("bzoj1190.in","r",stdin);
    11     freopen("bzoj1190.out","w",stdout);
    12     while(scanf("%d%d",&n,&m)!=EOF&&n!=-1){
    13         memset(cnt,0,sizeof(cnt));
    14         memset(w,0,sizeof(w));
    15         for(int i=1;i<=n;i++){
    16             int x,y=0,val;scanf("%d%d",&x,&val);
    17             while(!(x&1))x>>=1,y++;
    18             p[y][++cnt[y]]=x;q[y][cnt[y]]=val;
    19             w[y]+=x;
    20         }
    21         int len=0;while(m>>(len+1))len++;
    22         memset(f,0,sizeof(f));
    23         for(int i=0;i<=len;i++)
    24         for(int j=1;j<=cnt[i];j++)
    25         for(int k=w[i];k>=p[i][j];k--)f[i][k]=max(f[i][k],f[i][k-p[i][j]]+q[i][j]); //
    26         for(int i=1;i<=len;i++){
    27             w[i]+=(w[i-1]+1)>>1;
    28             for(int j=w[i];j>=0;j--)
    29             for(int k=0;k<=j;k++)f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min((k<<1)|((m>>(i-1))&1),w[i-1])]);
    30         }
    31         cout<<f[len][1]<<endl;
    32     }
    33     return 0;
    34 }//by tkys_Austin;
    View Code

     

    bzoj4753 最佳团体                      (01分数规划+树形依赖背包)

         题目大意:一个树形图,每个节点有价值和代价,只有当父亲选了才可以选,求最优的性价比;

        01分数规划套一个树形依赖背包就好,懒癌晚期,不再赘述;

        要求的答案好像比较诡异,记不得哪个是分子分母了。。。;

       

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<vector>
     5 #include<cmath>
     6 using namespace std;
     7 const int N=2505,inf=0x3f3f3f3f;
     8 int n,k,dfn[N],idx,sz[N],v[N],w[N],r[N];
     9 double f[N][N];
    10 vector<int>E[N];
    11 void dfs(int u){
    12     sz[u]=1;dfn[++idx]=u;
    13     for(int i=0;i<(int)E[u].size();i++){
    14         int v=E[u][i];
    15         dfs(v);
    16         sz[u]+=sz[v];
    17     }
    18 }
    19 int main()
    20 {   //freopen("bzoj4753.in","r",stdin);
    21     //freopen("bzoj4753.out","w",stdout);
    22     scanf("%d%d",&k,&n);
    23     for(int i=1;i<=n;i++){
    24         scanf("%d%d%d",&v[i],&w[i],&r[i]);
    25         E[r[i]].push_back(i);
    26     }
    27     dfs(0);
    28     for(int i=1;i<=k;i++)f[n+2][i]=-inf;
    29     double l=0,r=10000;
    30     while(fabs(l-r)>1e-4){
    31         double mid=(l+r)/2;
    32         for(int i=n+1;i>1;i--){
    33             int u=dfn[i];
    34         for(int j=1;j<=k;j++){
    35         f[i][j]=max(f[i+1][j-1]+w[u]-v[u]*mid,f[i+sz[u]][j]);
    36         }
    37     }
    38     if(f[2][k]>=0)l=mid;
    39     else r=mid;
    40     }
    41     printf("%.3lf
    ",l);
    42     return 0;
    43 }//by tkys_Austin;
    View Code

    bzoj4182 shopping                    (单调队列优化多重背包+点分治)

           题目大意:每个节点有库存d[i]个物品,每个物品价格c[i],价值w[i],买东西的节点一定是个联通块,求最大价值;

         连通块的话一定有一个分治中心在连通块上,对每个分治中心做一次树形依赖的多重背包;

         还是做dfs序,有一点注意就是多重背包要选的话就要强制塞一个物品再从f[i+1]转移;

         可以单调队列优化,也可以二分制分组优化;

        

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int N=510,M=4010;
     7 int o,n,m,hd[N],w[N],c[N],d[N],mx[N],sz[N],size,rt,vis[N],f[N][M],ans,tot,dfn[N],ed[N],head,tail,g[M];
     8 struct node{int x,j;}q[M];
     9 struct Edge{int v,nt;}E[N<<1];
    10 void Adde(int u,int v){E[o]=(Edge){v,hd[u]};hd[u]=o++;}//
    11 void adde(int u,int v){Adde(u,v);Adde(v,u);}//
    12 void get_rt(int u,int fa){
    13     sz[u]=1;mx[u]=0;
    14     for(int i=hd[u];~i;i=E[i].nt){
    15         int v=E[i].v;
    16         if(v==fa||vis[v])continue;
    17         get_rt(v,u);
    18         sz[u]+=sz[v];
    19         mx[u]=max(mx[u],sz[v]);
    20     }
    21     mx[u]=max(mx[u],size-sz[u]);
    22     if(mx[u]<mx[rt])rt=u;
    23 }//
    24 void dfs(int u,int fa){
    25     dfn[++tot]=u;
    26     for(int i=hd[u];~i;i=E[i].nt){
    27         int v=E[i].v;if(v==fa||vis[v])continue;
    28         dfs(v,u);
    29     }
    30     ed[u]=tot;
    31 }//
    32 void solve(int u){
    33     vis[u]=1;
    34     tot=0;dfs(rt,0);//
    35     int pre=size;//
    36     for(int i=0;i<=m;i++)f[tot+1][i]=0;//
    37     for(int I=tot;I;I--){
    38         int x=dfn[I];
    39         for(int i=0;i<=m;i++)f[I][i]=f[ed[x]+1][i];//,g[i]=0;
    40         //for(int i=0;i<=m-c[x];i++)g[i+c[x]]=f[I+1][i]+w[x];
    41         for(int i=0;i<c[x];i++){
    42         head=tail=0;int cnt=0;
    43         for(int j=c[x]+i;j<=m;j+=c[x],cnt++){
    44             while(head<tail&&cnt-q[head+1].j>d[x])head++;
    45             int T=f[I+1][j-c[x]]+w[x]-w[x]*cnt;
    46             while(head<tail&&q[tail].x<T)tail--;
    47             q[++tail]=(node){T,cnt};
    48             f[I][j]=max(f[I][j],q[head+1].x+cnt*w[x]);
    49         }
    50         }
    51     }//
    52     for(int i=0;i<=m;i++)ans=max(ans,f[1][i]);
    53     for(int i=hd[u],v=E[i].v;~i;i=E[i].nt,v=E[i].v){
    54         if(vis[v])continue;
    55         size=sz[u]>sz[v]?sz[v]:pre-sz[u];
    56         rt=0;get_rt(v,0);
    57         solve(rt);
    58     }//
    59 }//
    60 int main()
    61 {    freopen("bzoj4182.in","r",stdin);
    62     freopen("bzoj4182.out","w",stdout);
    63     int T;scanf("%d",&T);
    64     while(T--){
    65         scanf("%d%d",&n,&m);
    66         for(int i=1;i<=n;i++)scanf("%d",&w[i]),vis[i]=0;
    67         for(int i=1;i<=n;i++)scanf("%d",&c[i]),hd[i]=-1;
    68         for(int i=1;i<=n;i++)scanf("%d",&d[i]),d[i]--;
    69         o=0;for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),adde(x,y);//
    70         ans=0;size=mx[rt=0]=n;//
    71         get_rt(1,0);solve(rt);//
    72         cout<<ans<<endl;//
    73     } //
    74     return 0;
    75 }//by tkys_Austin; 
    View Code

     

     

     

     

     

     

     

     

     

     

     

     

     

    大米飘香的总结:

          最近改换了博客风格,以前可能是不太舍得大米兔的风格,或者说比较倔,感觉换了风格就不太对得起博客,省选过后的半年里感觉十分绝望,停更了一段时间,竞赛学习也有点滞留期,后面想清楚了,开始复习了也觉得自己应该写点,可能很多都以专题呈现,但是我本人变化比较大。。;我现在更希望用简洁但能引导思维的语言去经营博客,可能不会生动,但我想应该还是有希望写出很好的文章,帮帮更多的人(顺便骗取访问量->_<-);

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    animate()的使用
    jQuery UI中datepicker()的使用
    newsletter 在不同邮箱中发送的问题
    用户评价 打星等级 效果 jQuery
    SpringBoot整合WebSocket
    SpringBoot之自定义拦截器
    SpringBoot整合MyBatis
    EF实体框架简单原理及基本增删改查用法(上)
    数据库内容导出到Excel
    Asp.Net页面生命周期
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/9552728.html
Copyright © 2011-2022 走看看