题目
题解思路
参考这个文章和闫总的视频讲解
每个体积下的余数都对应着一个滑动窗口 , 用单调队列来进行优化处理
这样每个物体就只走了M次
在出入队时,要将体积差距下的W给补上。
方程推导是我将不明白的
AC代码
//单调队列优化多重背包 二维写法方便理解
//一维写法 使用拷贝数组
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
const int INF = 0x3f3f3f3f;
int f[20100],g[20100],q[20100];
int main ()
{
ios::sync_with_stdio(false);
int n,m;
cin>>n>>m;
for (int i = 1 ; i <= n ; i++ )
{
memcpy( g , f , sizeof ( g ) ); //将上一层f数组贴给g
int v,w,s;
cin>>v>>w>>s;
for (int j = 0 ; j < v ; j++ )
{
int hh = 0 , tt = -1 ;
for (int k = j ; k <= m ; k += v )
{
if ( hh <= tt && q[hh] < k - v*s )
hh++;
while( hh <= tt && g[q[tt]] + (k - q[tt])/v*w <= g[k] )
tt--;
tt++;
q[tt] = k ;
f[k] = g[q[hh]] + (k - q[hh] )/v*w;
}
}
}
cout<<f[m]<<"\n";
return 0 ;
}
//二维写法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
const int INF = 0x3f3f3f3f;
int s[1010],w[1010],v[1010];
int dp[1010][20100];
int q[20100];
int main ()
{
ios::sync_with_stdio(false);
int n ,vv ;
cin>>n>>vv;
for (int i = 1 ; i <= n ; i++ )
cin>>v[i]>>w[i]>>s[i];
for (int i = 1 ; i <= n ; i++ )
{
for (int r = 0 ; r < v[i] ; r++ ) //使用单调队列对每一个余数进行优化
{
int hh = 0 , tt = -1 ;
for( int p = r ; p <= vv ; p += v[i] )
{
if ( hh <= tt && q[hh] < p - s[i]*v[i]) //编号是否小于滑动窗口的最小范围
hh++;
while( hh <= tt && dp[i-1][q[tt]] + ( p - q[tt])/v[i] * w[i] <= dp[i-1][p] ) //先加上相差的w值再进行大小比较
tt--;
tt++;
q[tt] = p ;
dp[i][p] = dp[i-1][q[hh]] + ( p - q[hh ])/v[i]*w[i] ; //取队头最大元素并加上差值
}
}
}
cout<<dp[n][vv];
return 0 ;
}