1. 程式人生 > >洛谷P4095||bzoj3163 [HEOI2013]Eden 的新揹包問題

洛谷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\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 }
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\n",ans[i]);
69     return 0;
70 }
View Code