洛谷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

猜你喜欢

转载自www.cnblogs.com/hehe54321/p/9915694.html