典型的單調隊列最佳化dp。
對於n類物品,某類物品的數量為k,價值為v,容量為w。
那麼考慮到這個種類的物品的時候有如下dp方程:
設dp[i]表示容量為i的時候得到的最大價值,那麼我們就有:
dp[i]=max{dp[i-k*w]+k*v}
換一種寫法:
dp[mod+k*w]=max{dp[mod+j*w]+(k-j)*v}
這裡為了方便,我們用s[j]表示dp[mod+j*w],所以就有
dp[mod+k*w]=max{s[j]+(k-j)*v}=max{s[j]-j*v}+k*v
這裡k已經是定值,也就是說dp[mod+k*w]只和一定範圍內的s[j]-j*v的最大值有關,可以用單調隊列最佳化。
總的複雜度是O(N*V)的。
這道題比較簡單,由於價值都是1的,我們就直接開一個bool型數組儲存是否存在解就可以了。然後枚舉到的k如果dp[k]是可達的,那麼我們就將其id入隊,否則就不管,另外由於價值一樣,所以不要記錄價值。
此類背包問題還可以進一步地最佳化,如果k等於1的時候直接寫0-1背包,如果物品可以取得的總容量大於背包總容量,那麼就直接寫完全背包,因為這兩種寫法和單調隊列相比常數小了非常多。
最後考慮到很大的輸入輸出量,選擇C++編譯器。
完整代碼:
#include <cstdio><br />#include <cstring><br />#include <cstdlib><br />#include <algorithm><br />using namespace std;<br />const int MAX=100010;<br />bool dp[MAX];<br />int id[MAX],c[120],v[120],f,b;<br />int main(){<br />int n,m;<br />freopen("in.txt","r",stdin);</p><p>while(scanf("%d%d",&n,&m),n||m){<br />for(int i=1;i<=m;i++)dp[i]=false;dp[0]=true;<br />for(int i=0;i<n;i++)scanf("%d",&v[i]);<br />for(int i=0;i<n;i++)scanf("%d",&c[i]);<br />for(int i=0;i<n;i++){<br />int w=c[i]*v[i];<br />if(c[i]==1){<br />for(int j=m;j>=v[i];j--){<br />if(dp[j-v[i]])dp[j]=true;<br />}<br />}else if(w>=m){<br />for(int j=v[i];j<=m;j++){<br />if(dp[j-v[i]])dp[j]=true;<br />}<br />}else{<br />for(int j=0;j<v[i];j++){<br />f=b=0;<br />for(int k=j;k<=m;k+=v[i]){<br />if(f!=b&&k-id[f]>w)f++;<br />if(dp[k]){<br />id[b++]=k;<br />}else if(f!=b){<br />dp[k]=true;<br />}<br />}<br />}<br />}<br />}<br />int ret=0;<br />for(int i=1;i<=m;i++)if(dp[i])ret++;<br />printf("%d/n",ret);<br />}<br />return 0;<br />}<br />