【Codeforces 500F】Dp

F题还是挺有趣的。一个商店有n个物品,和一个时限p,每个物品有ci, vi, ti,这个物品只会在时刻ti到ti+p-1出现,价值vi,花费ci。

有Q次询问,每次询问有ai,bi,表示如果在时刻ai到商店,带钱bi,能得到的最大价值,每个物品只能买一个。

n <= 4000, Q <= 10000, ai, bi <= 4000, ai <= 20000, p <= 10000.

这一看就是01背包问题,如果对于每个询问暴力是O(n*q*W)的,需要优化。

发现可以把物品看作只在ti出现,询问是ai-p+1到ai在商店。

每个询问长度都是p,所以可以把时间分为[1, p], [p, 2p], [2p, 3p] ……这样在每个小块中从左向右再从右向左dp,设分界点为d,询问被拆成两块,合并是O(bi) = O(W)的。

总复杂度O((n+q) * W)。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
using namespace std;
typedef long long LL;
const int N = 4001, W = 20000;
struct arr { int c, v, t; } a[N];
int n, l_r[N][N], r_l[N][N], p, Q, ans;
bool Cmp(arr x, arr y) { return x.t < y.t; }
void Prework() {
	int j = 1;
	for (int i = 1; (i - 1) * p <= W; i ++) {
		for (; j <= n && a[j].t <= i * p; j ++) {
			Dwn(k, N-1, a[j].c) {
				if (a[j-1].t > (i-1) * p) l_r[j][k] = max(l_r[j-1][k - a[j].c] + a[j].v, l_r[j-1][k]);
				else l_r[j][k] = a[j].v;
			}
			if (a[j-1].t > (i-1) * p) Dwn(k, a[j].c - 1, 1) l_r[j][k] = l_r[j-1][k];
		}
		int j0 = j;
		for (j = j - 1; j > 0 && a[j].t > (i - 1) * p; j --) {
			Dwn(k, N-1, a[j].c) {
				if (j < n && a[j+1].t <= i * p) r_l[j][k] = max(r_l[j+1][k - a[j].c] + a[j].v, r_l[j+1][k]);
				else r_l[j][k] = a[j].v;
			}
			if (j < n && a[j+1].t <= i * p) Dwn(k, a[j].c - 1, 1) r_l[j][k] = r_l[j+1][k];
		}
		j = j0;
	}
}
int main()
{
	scanf ("%d%d", &n, &p);
	Rep(i, 1, n) scanf ("%d%d%d", &a[i].c, &a[i].v, &a[i].t);
	sort(a+1, a+n+1, Cmp);
	Prework();
	scanf("%d", &Q);
	while (Q --) {
		int lx, rx, d, ly, ry, y;
		scanf ("%d%d", &rx, &y);
		lx = rx - p + 1;
		d = (rx - 1) / p * p;
		ly = n + 1, ry = 0;
		Rep(i, 1, n) if (a[i].t >= lx) { ly = i; break ; }
		Dwn(i, n, 1) if (a[i].t <= rx) { ry = i; break ; }
		if (ly > ry) { puts("0"); continue ; }
		ans = 0;
		if (a[ly].t > d) ans = l_r[ry][y];
		else if (a[ry].t <= d) ans = r_l[ly][y];
		else {
			Rep(i, 0, y) {
				ans = max(ans, l_r[ry][i] + r_l[ly][y-i]);
			}
		}
		printf("%d\n", ans);
	}
	
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值