bzoj 3379

Description

一个数轴上有 (n le 1000) 个位置, 每个位置有一个时间 (t_i)

要求在 时刻 (t_i) 后, 至少经过该位置一次. (去交作业)

求从 (0) 号点出发前往 (B) 号点, 满足上述条件的最小需要时间

Solution 1

CQzhangyu的做法. 非常的妙啊

(ge t) 不好做, 走过的位置以后可能又会突然冒出一个人, 决策的顺序位置.

考虑反着来, 先二分答案 (Ans), 并从 (B)(0) 走.

这样限制条件变成了 (le Ans - t_i), 这样就之用在限制时间内经过一次该点即可

直接使用区间dp, 复杂度 (O(n^2log n))

Solution 2

考虑初始状态 ([1, n]) 区间均未交作业

接着你会进去区间内乱走一通, 交一些作业. 且此时你一直没有交边界的作业

但是边界的作业迟早是要交的, 两个边界中, 必有一个是 先被到达的

不妨设 ([a,b]) 先到达了 (a), 那么接下来, (b) 一定是要过去的, 中间的点都是必须被经过的.

而这些中间被经过的, 若是在乱走到达 (a) 之前交了作业, 将其换至 到 (a) 后往 (b) 走的过程中再交是没有影响的.

于是, 最优的决策, 一定是选择区间的一端 先交作业

接着就可以使用区间dp, 区间从大往小走就好了

Notice

坐标有 (0)

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define rep(i, a, b) for (int i = (a), _ = (b); i <= _; ++ i)
#define per(i, a, b) for (int i = (a), _ = (b); i >= _; -- i)
#define For(i, a, b) for (int i = (a), _ = (b); i < _; ++ i)
#define ri rd<int>
using namespace std;
const int N = 1007;
const int INF = 1e9 + 7;

template<class T> inline T rd() {
	bool f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
	T x = 0; for (; isdigit(c); c = getchar()) x = x * 10 + c - 48; return f ? x : -x;
}

int m, n, cur;
int t[N];
int f[N][N];
int lf, rt;

int main() {
#ifndef ONLINE_JUDGE
	freopen("a.in", "r", stdin);
#endif

	m = ri(), n = ri(), cur = ri() + 1;

	lf = n + 1, rt = 0;
	rep (i, 1, m) {
		int x = ri() + 1, y = ri();
		t[x] = max(t[x], y);
		lf = min(lf, x), rt = max(rt, x);
	}
	
	memset(f, 0x3f, sizeof f);
	f[lf][rt] = max(lf - 1, t[lf]), f[rt][lf] = max(rt - 1, t[rt]);

	per (l, rt-lf-1, 0) rep (i, lf, rt - l) {
		int j = i + l;
		f[i][j] = min(f[i][j], max(t[i], min(f[i-1][j] + 1,	f[j+1][i] + ((j + 1) - i))));
		f[j][i] = min(f[j][i], max(t[j], min(f[i-1][j] + (j - (i - 1)), f[j+1][i] + 1)));
	}
	
	int res = INF;
	rep (i, lf, rt) res = min(res, f[i][i] + abs(i - cur));
	printf("%d
", res);

	return 0;
}
原文地址:https://www.cnblogs.com/acha/p/7768347.html