牛客练习赛79E小G的数学难题
https://ac.nowcoder.com/acm/contest/11169/E
挺有意思的一道题
可以考虑放缩
可以把每个
a
i
a_i
ai放大,若仍然满足条件,那原问题也满足条件
同理把每个
b
i
b_i
bi缩小,若仍然满足条件,那原问题也满足条件
考虑一种特殊的情况,放缩使得
a
i
=
b
i
a_i=b_i
ai=bi那样就只用考虑一个数组了
那对于原来的转移方程,设
f
[
j
]
表
示
和
为
j
的
方
案
中
最
小
的
c
[
i
]
的
和
f[j]表示和为j的方案中最小的c[i]的和
f[j]表示和为j的方案中最小的c[i]的和
那么显然
f
[
j
]
=
m
i
n
(
f
[
k
]
+
c
[
i
]
)
(
k
∈
[
j
−
b
[
i
]
,
j
−
a
[
i
]
]
)
f[j]=min(f[k]+c[i])(k∈[j-b[i],j-a[i]])
f[j]=min(f[k]+c[i])(k∈[j−b[i],j−a[i]])
拿一个单调队列维护即可
code:
#include<bits/stdc++.h>
#define N 10050
using namespace std;
int n, m, a[N], b[N], c[N], q[N], f[N], g[N], t;
int main() {
scanf("%d", &t);
while(t --) {
scanf("%d%d", &n, &m);
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, 0x3f, sizeof f);
int INF = f[233];
f[0] = 0;
for(int i = 1; i <= n; i ++) {
memset(g, 0x3f, sizeof g);
int l = 1, r = 0;
for(int j = a[i]; j <= m; j ++) {
while(l <= r && f[q[r]] >= f[j - a[i]]) r --;
while(l <= r && q[l] < j - b[i]) l ++;
q[++ r] = j - a[i];
if(f[q[l]] != INF) g[j] = f[q[l]] + c[i];
}
for(int j = a[i]; j <= m; j ++) f[j] = min(f[j], g[j]);
}
if(f[m] == INF) printf("IMPOSSIBLE!!!\n");
else printf("%d\n", f[m]);
}
return 0;
}