0x01 题意
对于三个长度为 的数组
,确定一个数组
满足
,使得:
并最小化:
0x02 思路
对于任何一种满足条件的 数组,我们都可以通过放缩
的方式使得条件①,②均取到等号
这里考虑其中一种特殊的放缩方式,使得所有的
关于放缩
对于每一个
,由于对
的限制条件是
,故若
放大之后答案存在,则原答案也存在
对于每一个,由于对
的限制条件是
,故若
缩小之后答案存在,则原答案也存在
因此对于每一组 ,将其拆分为
个三元组
,每个三元组中的三个数对应原有的
这样我们就去掉了 和
的限制,转化为
将每个 拆分出的
个三元组看作一层,跑一个分组背包即可
分组背包的复杂度是 的,并不能通过此题
每一层转移时, 的转移方程是
,
是一段连续的区间,可以用单调队列优化
时间复杂度
0x03 代码
#include <stdio.h>
#include <string.h>
#include <queue>
#define MP 10000
#define MN 1000
int n,p,a[MN+5],b[MN+5],c[MN+5],f[MP+5];
void solve(){
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
memset(f,0x7f,sizeof(f));
f[0] = 0;
for(int i=1;i<=n;i++){
std::deque<int> q;
for(int j=p-a[i];j>=p-b[i]&&j>=0;j--){
while(q.empty()==false&&f[j]<f[q.back()]) q.pop_back();
q.push_back(j);
}
for(int j=p;j>=0;j--){
if(!q.empty()){
f[j] = std::min(f[j],f[q.front()]+c[i]);
if(q.front()==j-a[i]) q.pop_front();
}
if(j-b[i]-1>=0){
while(q.empty()==false&&f[j-b[i]-1]<f[q.back()]) q.pop_back();
q.push_back(j-b[i]-1);
}
}
}
if(f[p]==0x7f7f7f7f){
puts("IMPOSSIBLE!!!");
}else{
printf("%d\n",f[p]);
}
}
int main(){
int T; scanf("%d",&T);
while(T--) solve();
} 
京公网安备 11010502036488号