牛客练习赛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]jc[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[jb[i],ja[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;
}

posted @ 2021-03-28 09:27  lahlah  阅读(31)  评论(0编辑  收藏  举报