2023牛客暑期多校第五场 H.Nazrin the Greeeeeedy Mouse

文章目录

  • 题目大意
  • 题解
        • 去除容量
        • 特殊条件
  • 参考代码
  • 总结

题目大意

一个小老鼠有 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>szi1
1 ≤ n , a i , s z i ≤ 200 1\leq n,a_i,sz_i \leq 200 1n,ai,szi200
1 ≤ m , b i ≤ 1 0 5 1\leq m,b_i \leq 10^5 1m,bi105

题解

一道明显的DP题,如果只看一次机会就是裸的01背包。
考虑枚举机会转移的时机,将容量,当前位置写进转移式子,
这是一个裸暴力(悲)。
转移的复杂度大致可以估算为 O ( N 3 ∗ m ) O(N^3*m) ON3m
时间明显超标。

去除容量

我们转念一想,容量对于最大值的计算实际上帮助不大,
由于数据在 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,r1,szar+br,fl,r1,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(dpx1,y+fy+1,i,szi)(0yi)
复杂度为 O ( N 2 ∗ m ) O(N^2 *m) O(N2m)

特殊条件

我们发现 s z sz sz的值是递增的,单独考虑,一个大的背包造成的贡献一定优于或等于一个小的背包。
所以我们只需要选取最后 n n n 组背包求解即可。
复杂度优化为 O ( N 3 ) O(N^3) O(N3)

参考代码

#include
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);
}

总结

注意看题目给出的特殊的数据。

你可能感兴趣的:(动态规划)