题目大意
一个小老鼠有
m
m
m 次偷东西机会,每次背包大小为
s
z
i
sz_i
szi ,有
n
n
n 件物品,大小
a
i
a_i
ai ,价值
b
i
b_i
bi ,每次只能顺序拿去(或不拿),求最大的价值和。
保证
s
z
i
>
s
z
i
−
1
sz_i>sz_{i-1}
szi>szi−1
1
≤
n
,
a
i
,
s
z
i
≤
200
1\leq n,a_i,sz_i \leq 200
1≤n,ai,szi≤200
1
≤
m
,
b
i
≤
1
0
5
1\leq m,b_i \leq 10^5
1≤m,bi≤105
题解
一道明显的DP题,如果只看一次机会就是裸的01背包。
考虑枚举机会转移的时机,将容量,当前位置写进转移式子,
这是一个裸暴力(悲)。
转移的复杂度大致可以估算为
O
(
N
3
∗
m
)
O(N^3*m)
O(N3∗m)。
时间明显超标。
去除容量
我们转念一想,容量对于最大值的计算实际上帮助不大,
由于数据在
200
200
200 ,
我们可以提前打出某容量在一段区间里的最大价值。
容易想到
f
l
,
r
,
s
z
=
m
a
x
(
f
l
,
r
−
1
,
s
z
−
a
r
+
b
r
,
f
l
,
r
−
1
,
s
z
)
f_{l,r,sz}=max(f_{l,r-1,sz-a_r}+b_r,f_{l,r-1,sz})
fl,r,sz=max(fl,r−1,sz−ar+br,fl,r−1,sz) 。
容量的计算就可以舍去了。
我们重新定义DP式子,将机会和当前位置写入转移式。
容易得到
d
p
x
,
i
=
m
a
x
(
d
p
x
−
1
,
y
+
f
y
+
1
,
i
,
s
z
i
)
(
0
≤
y
≤
i
)
dp_{x,i}=max(dp_{x-1,y}+f_{y+1,i,sz_i})(0\leq y\le i)
dpx,i=max(dpx−1,y+fy+1,i,szi)(0≤y≤i)
复杂度为
O
(
N
2
∗
m
)
O(N^2 *m)
O(N2∗m)
特殊条件
我们发现
s
z
sz
sz的值是递增的,单独考虑,一个大的背包造成的贡献一定优于或等于一个小的背包。
所以我们只需要选取最后
n
n
n 组背包求解即可。
复杂度优化为
O
(
N
3
)
O(N^3)
O(N3)
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int dp[N][N];
int f[N][N][N];
int a[N],b[N];
int sz[N];
int n,m,tot,ans;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
for(int i=1;i<=m;i++)
{
int a;
scanf("%d",&a);
if(i+n>=m)
sz[++tot]=a; //递增的特殊性
}
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++)
{
for(int siz=a[r];siz<=200;siz++) //预处理容量
f[l][r][siz]=max(f[l][r-1][siz-a[r]]+b[r],f[l][r-1][siz]);
for(int k=0;k<=200;k++)
f[l][r][k]=max(f[l][r][k],f[l][r-1][k]);
}
for(int i=1;i<=tot;i++)
for(int j=1;j<=n;j++)
for(int y=0;y<=j;y++)
{
dp[i][j]=max(dp[i-1][y]+f[y+1][j][sz[i]],dp[i][j]);
ans=max(dp[i][j],ans);
}
printf("%d",ans);
}
总结
注意看题目给出的特殊的数据。