zoukankan      html  css  js  c++  java
  • 洛谷P4095||bzoj3163 [HEOI2013]Eden 的新背包问题

    https://www.luogu.org/problemnew/show/P4095

    不太会。。

    网上有神奇的做法:

    第一种其实是暴力(复杂度3e8...)然而可以A。考虑多重背包,发现没有办法快速删除某个物品造成的贡献。考虑对于每个i,求出an1[i]和an2[i],分别表示对于[1,i]和[i,n]区间内所有物品的答案数组(如an1[i][j]表示[1,i]区间内用掉容量j可以带来的最大贡献),这个就是用普通多重背包求出来(可能要优化一下多重背包,以下用了二进制优化)。每次询问(x,y),就暴力枚举在[1,x-1]和[x+1,n]区间内分别取多少容量的物品,取最大贡献。复杂度O(n*log(c)*e+q*e)(如果用二进制优化多重背包)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 int n,n1;
    14 int c[10010],d[10010];
    15 int lp[1010],rp[1010];
    16 void work(int x,int y,int z)
    17 {
    18     //printf("1t%d %d %d %d
    ",x,y,z,k);
    19     ++n1;x*=z;y*=z;
    20     c[n1]=x;d[n1]=y;
    21     //printf("1t%d %d
    ",c[n1],d[n1]);
    22 }
    23 int qq;
    24 int ans;//int ans[10010];
    25 int an1[10010][1010];//an1[i][j]表示(前i个物品)j的容量最大价值
    26 int an2[10010][1010];//i及之后的物品...
    27 int main()
    28 {
    29     int i,j,x,y,z;
    30     scanf("%d",&n);
    31     for(i=1;i<=n;++i)
    32     {
    33         scanf("%d%d%d",&x,&y,&z);
    34         lp[i]=n1+1;
    35         for(j=1;(j<<1)-1<=z;j<<=1)
    36             work(x,y,j);
    37         if(z-j+1)    work(x,y,z-j+1);
    38         rp[i]=n1;
    39     }
    40     for(i=1;i<=n1;++i)
    41     {
    42         memcpy(an1[i],an1[i-1],sizeof(an1[i]));
    43         for(j=1000;j>=c[i];--j)
    44             an1[i][j]=max(an1[i][j],an1[i][j-c[i]]+d[i]);
    45     }
    46     for(i=n1;i>=1;--i)
    47     {
    48         memcpy(an2[i],an2[i+1],sizeof(an2[i]));
    49         for(j=1000;j>=c[i];--j)
    50             an2[i][j]=max(an2[i][j],an2[i][j-c[i]]+d[i]);
    51     }
    52     scanf("%d",&qq);
    53     for(i=1;i<=qq;++i)
    54     {
    55         scanf("%d%d",&x,&y);++x;
    56         ans=0;
    57         for(j=0;j<=y;++j)
    58             ans=max(ans,an1[rp[x-1]][j]+an2[lp[x+1]][y-j]);
    59         printf("%d
    ",ans);
    60     }
    61     return 0;
    62 }
    View Code

    第二种是神奇的分治。注意到既不能快速删除多重背包中某个物品造成的贡献,又不能快速合并两个多重背包,但是可以快速向多重背包中加入一个物品。分治时维护一个多重背包的答案数组。solve(l,r),就先把[l,mid]内部的物品加入多重背包,然后solve(mid+1,r),再去掉[l,mid]内部物品造成的贡献(只需要恢复这一步操作之前的贡献数组即可),然后将[mid+1,r]内部的物品加入多重背包,solve(l,mid),去掉[mid+1,r]内部造成的贡献。这样当进行到solve(l,l)时就恰好只有l这个物品自身没有加入多重背包了,此时对于所有对这个位置的询问处理一下即可。复杂度O(n*log(c)*e*log(n)+q)(如果用二进制优化多重背包)(然而以下代码洛谷上跑的比“暴力”慢???)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 int n,n1;
    14 int c[10010],d[10010];
    15 int lp[1010],rp[1010];
    16 void work(int x,int y,int z)
    17 {
    18     ++n1;x*=z;y*=z;
    19     c[n1]=x;d[n1]=y;
    20 }
    21 vector<pii> q[1010];
    22 int qq;
    23 int an[1010];int ans[300010];
    24 void solve(int l,int r)
    25 {
    26     int i,j;
    27     if(l==r)
    28     {
    29         for(i=0;i<q[l].size();++i)
    30             ans[q[l][i].se]=an[q[l][i].fi];
    31         return;
    32     }
    33     int mid=l+((r-l)>>1);
    34     int an2[1010];
    35     memcpy(an2,an,sizeof(an2));
    36     for(i=lp[l];i<=rp[mid];++i)
    37         for(j=1000;j>=c[i];--j)
    38             an[j]=max(an[j],an[j-c[i]]+d[i]);
    39     solve(mid+1,r);
    40     memcpy(an,an2,sizeof(an));
    41     for(i=lp[mid+1];i<=rp[r];++i)
    42         for(j=1000;j>=c[i];--j)
    43             an[j]=max(an[j],an[j-c[i]]+d[i]);
    44     solve(l,mid);
    45     memcpy(an,an2,sizeof(an));
    46 }
    47 int main()
    48 {
    49     int i,j,x,y,z;
    50     scanf("%d",&n);
    51     for(i=1;i<=n;++i)
    52     {
    53         scanf("%d%d%d",&x,&y,&z);
    54         lp[i]=n1+1;
    55         for(j=1;(j<<1)-1<=z;j<<=1)
    56             work(x,y,j);
    57         if(z-j+1)    work(x,y,z-j+1);
    58         rp[i]=n1;
    59     }
    60     scanf("%d",&qq);
    61     for(i=1;i<=qq;++i)
    62     {
    63         scanf("%d%d",&x,&y);++x;
    64         q[x].pb(pii(y,i));
    65     }
    66     solve(1,n);
    67     for(i=1;i<=qq;++i)
    68         printf("%d
    ",ans[i]);
    69     return 0;
    70 }
    View Code
  • 相关阅读:
    ASP.NET Core 静态资源的打包与压缩
    算法
    字符串反转
    js 获取随机数
    AspNetCore MVC 跨域
    add digits
    1-bit and 2-bit Characters
    删除字符串中出现次数最少的字符
    洗牌
    哈夫曼编码
  • 原文地址:https://www.cnblogs.com/hehe54321/p/9915694.html
Copyright © 2011-2022 走看看