洛谷P4095||bzoj3163 [HEOI2013]Eden 的新揹包問題
阿新 • • 發佈:2018-11-06
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;View Code12 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\n",x,y,z,k); 19 ++n1;x*=z;y*=z; 20 c[n1]=x;d[n1]=y; 21 //printf("1t%d %d\n",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\n",ans); 60 } 61 return 0; 62 }
第二種是神奇的分治。注意到既不能快速刪除多重揹包中某個物品造成的貢獻,又不能快速合併兩個多重揹包,但是可以快速向多重揹包中加入一個物品。分治時維護一個多重揹包的答案陣列。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\n",ans[i]); 69 return 0; 70 }View Code