zoukankan      html  css  js  c++  java
  • 极大容量的完全背包问题

    题目大意:

    就是题目名称的意思,有n种物品,一个容量为m的背包,每种物品的体积为$ a_i $,价值为$ b_i $,有$ n<=10^6,m<=10^{18},a_i,b_i<=100 $。求最大价值。

    解题方法:

    因为m很大,所以我们考虑将较大的体积为S的背包分为较小的两个背包,其中一个体积为x,则另一个S-x。

    这时讨论abs(x-(S-x))的范围,可以推出其<=maxv(物品的最大体积)。因为单独对两个背包操作,两边会有余留下来的部分,将其中一个余留下来的部分补到另一个去可能会得到更忧解。但如果移动的部分大于maxv了则放在两个背包中的任意一个都可以得到最优解。所以我们成功将枚举区间缩小到了$ frac{S-maxv} {2} <=x<= frac{S+maxv} {2} $。

    然后我们类似分治的套路将图画出来:

    发现$ Max -Min < 2 imes maxv $并且最底层(即$ Min>0 $的最后一层)的$ Min <= maxv,Max<=3 imes maxv $,所以我们可以预处理出$ 3 imes maxv $大小的背包就可以得到最底层的解,然后我们可以记录每层最小值$ L[i] $即最左边的点的权值,设$ g[i][j] $表示在第i层时比$ L[i] $容量大j的背包的最优解,枚举x逐层向上转移即可,转移方程:$ g[i][v-L[i]]=max(g[i+1][x-L[i+1]]+g[i+1][v-x-L[i+1]]) $。

    因为一开始给了$ 10 ^ 6$种物品,然而因为最多有$ 100 imes 100 $种物品,所以可以用set去重。

    代码:

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define del(a,b) memset(a,b,sizeof(a))
     4 using namespace std;
     5 const int MAXN=1e5+10;
     6 ll n,m,tot;
     7 set<int> ap[110];
     8 ll f[MAXN],g[55][MAXN],L[55],a[MAXN],b[MAXN];
     9 void Max(ll &a,ll b){a=(a>b? a:b);}
    10 template <class T>void read(T &x)
    11 {
    12     bool f=0;char ch=getchar();x=0;
    13     for(;ch<'0' || ch>'9';ch=getchar())if(ch=='-') f=1;
    14     for(;ch>='0' && ch<='9';ch=getchar())x=x*10+ch-'0';
    15     if(f) x=-x;
    16 }
    17 int main()
    18 {
    19     read(n);read(m);
    20     ll maxv=0;
    21     for(int i=1;i<=n;i++)
    22     {
    23         ll x,y;read(x);read(y);
    24         if(ap[x].find(y)!=ap[x].end()) continue;
    25         a[++tot]=x;b[tot]=y;ap[x].insert(y);
    26         Max(maxv,a[tot]);
    27     }
    28     ll s=m,cnt=0;
    29     while(s>0)
    30     {
    31         L[++cnt]=s;
    32         s=(s-maxv)>>1;
    33     }
    34     for(int i=1;i<=tot;i++)
    35     for(int v=a[i];v<=maxv*3;v++)
    36         Max(f[v],f[v-a[i]]+b[i]);
    37     for(int i=cnt;i>=1;i--)
    38     for(ll v=L[i];v<=L[i]+maxv*2;v++)
    39     {
    40         if(v<=maxv*3) g[i][v-L[i]]=f[v];
    41         else
    42         {
    43             for(ll x=(v-maxv)>>1;x<=v>>1;x++)
    44             Max(g[i][v-L[i]],g[i+1][x-L[i+1]]+g[i+1][v-x-L[i+1]]);
    45         }
    46     }
    47     printf("%lld",g[1][0]);
    48     return 0;
    49 }
    View Code
  • 相关阅读:
    Android中的IMEI
    《JAVA与模式》之适配器模式(转)
    海量日志数据__怎么在海量数据中找出重复次数最多的一个
    Java中的IO流系统详解(转载)
    获取网络文件长度问题
    内存泄漏
    Ubuntu12.04不能连接小米开发(转)
    Java/C++中数组的区别
    Android批量插入数据到SQLite数据库
    泛型编程 基础
  • 原文地址:https://www.cnblogs.com/Oracle-LinJH/p/9827067.html
Copyright © 2011-2022 走看看