2023牛客暑期多校训练营5-H Nazrin the Greeeeeedy Mouse
题目描述
解题思路
仅看题目,我们可以想到01背包,但是仔细一看,dp可能会包含多个需要维护的值,跑01很显然会炸。
我们仔细看题目,会发现这个背包容量sz[i]是单调递增的,而m的范围是[1,1e5] 为了缩短时间,我们可以只取最后200个包。此时我们的时间是O(
n
3
n^3
n3)
先处理出
[
l
,
r
]
[l,r]
[l,r]区间内用
s
s
s 能获得的最大芝士重量
f
i
,
j
,
s
=
m
a
x
(
f
i
,
j
−
1
,
s
−
a
j
+
b
j
,
f
i
,
j
−
1
,
s
)
f_{i,j,s}=max(f_{i,j-1,s-a_j}+b_j,f_{i,j-1,s})
fi,j,s=max(fi,j−1,s−aj+bj,fi,j−1,s)
可以设dp的状态为走的步数和当前位置
转移方程式为:
d
p
i
,
j
=
m
a
x
(
d
p
i
−
1
,
y
+
f
y
+
1
,
j
,
s
z
i
,
d
p
i
,
j
)
dp_{i,j}=max(dp_{i-1,y}+f_{y+1,j,sz_i},dp_{i,j})
dpi,j=max(dpi−1,y+fy+1,j,szi,dpi,j)
其中的y表示从y走到x,枚举y从1到x 再在循环中统计dp最大值
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+2;
const int M=2e2+2;
int n,m;
int dp[M][M];
int f[M][M][M];
int a[M],b[M];
int pos[M];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
int x,cnt=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
if(i+n>=m) pos[++cnt]=x;//只记录最后n个包
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
for(int s=a[j];s<=200;s++)
f[i][j][s]=max(f[i][j-1][s-a[j]]+b[j],f[i][j-1][s]);
for(int s=0;s<=200;s++)//比较是否跳过该芝士的最优解
f[i][j][s]=max(f[i][j-1][s],f[i][j][s]);
}
int l,r,s;
// while(cin>>l>>r>>s)
// {
// cout<<f[l][r][s]<<" \n";
// }
int ans=0;
for(int i=1;i<=cnt;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][pos[i]],dp[i][j]);
ans=max(ans,dp[i][j]);
}
}
}
printf("%d",ans);
}