JZOJ 6309. 完全背包
题目
Description
Input
Output
Sample Input
Sample 1:
2 15
3 2
5 3
Sample 2:
3 70
71 100
69 1
1 2
Sample Output
Sample 1:
10
Sample 2:
140
Data Constraint
题解
- 乍一看,这不就是非常裸的完全背包吗???
- 再看看范围,背包容量极大,但是,物品的代价却很小。
- 如果直接打最裸的完全背包,时间复杂度是 O ( n m ) O(nm) O(nm),能得到 20 20 20分;
- 如果对于代价相同的,只用收益最大的一个转移,时间复杂度是 O ( M a x a i m ) O(Max_{a_i}m) O(Maxaim),能得到 60 60 60分;
- 看到这么大的 m m m,不难想到矩阵。
- 设初始一维矩阵 v [ i ] ( i ∈ [ 0 , 100 ] ) v[i](iin [0,100]) v[i](i∈[0,100])表示花费 i i i的代价最大能有多少收益(可以选多个,如同容量为 i i i的完全背包),
- 不难发现,有了这个就可以用两个 i ∈ [ 0 − 100 ] iin[0-100] i∈[0−100]得到 i ∈ [ 100 , 200 ] iin[100,200] i∈[100,200],用两个 i ∈ [ 100 , 200 ] iin [100,200] i∈[100,200]得到 i ∈ [ 300 , 400 ] iin [300,400] i∈[300,400]……以此类推。
- 那么自然想到矩阵快速幂,因为每次可以得出 100 100 100个的答案,所以快速幂的指数设为 ⌊ m 100 ⌋ + 1 lfloorfrac{m}{100} floor+1 ⌊100m⌋+1,
- 每次先用当前的矩阵 f f f与自己相加取 m a x max max,然后用 v v v更新一遍 f f f,
- 如果指数为奇数,再用 f f f与 v v v相加得到新的 f f f,
- 最后的答案即为 f [ m m o d 100 ] f[mmod 100] f[mmod100]。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll v[110],f[110],c[110];
void ksm(ll x)
{
int i,j;
if(x==1)
{
for(i=0;i<=100;i++) f[i]=v[i];
return;
}
ksm(x/2);
memset(c,0,sizeof(c));
for(i=0;i<=100;i++)
for(j=0;j<=100;j++)
if(i+100-j>=0&&i+100-j<=100) c[i]=max(c[i],f[j]+f[i+100-j]);
for(i=0;i<=100;i++)
for(j=0;j<=i;j++) c[i]=max(c[i],c[j]+v[i-j]);
for(i=0;i<=100;i++) f[i]=c[i];
if(x%2)
{
memset(c,0,sizeof(c));
for(i=0;i<=100;i++)
for(j=0;j<=100;j++)
if(i+100-j>=0&&i+100-j<=100) c[i]=max(c[i],f[j]+v[i+100-j]);
for(i=0;i<=100;i++)
for(j=0;j<=i;j++) c[i]=max(c[i],c[j]+v[i-j]);
for(i=0;i<=100;i++) f[i]=c[i];
}
}
int main()
{
int n,i,j,x,y;ll m;
scanf("%d%lld",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(y>v[x]) v[x]=y;
}
for(i=1;i<=100;i++)
for(j=1;j<i;j++) v[i]=max(v[i],v[j]+v[i-j]);
ksm(m/100+1);
printf("%lld",f[m%100]);
return 0;
}