極大容量的完全揹包問題
阿新 • • 發佈:2018-10-31
題目大意:
就是題目名稱的意思,有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\times maxv $並且最底層(即$ Min>0 $的最後一層)的$ Min <= maxv,Max<=2 \times maxv $,所以我們可以預處理出$ 3\times 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\times 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];View Code9 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 }