Atcode120E 1D Party

题面

Atcode120E 1D Party_第1张图片
题目链接

解题思路

我们能够观察出如下性质:
1.每个人要么一开始往左,再一直往右,要么一开始往右再一直往左。
2.每个人都是在不停的运动。
我们可以二分一个时间t,考虑在t时间内相邻的两个人是否都能够碰面一次。
我们发现每个人只会有两种策略,考虑使用dp消除两种策略的前效性。
dp1[i]表示在t时刻,第i个人先向左i-1碰面后往右能够到达的最远位置。
dp2[i]表示第i个人在t时刻和i-1碰面且一开始能够向右走dp2[i]的时间。
转移即可。
复杂度O(nlogn)。

代码

#include 
#include 
#include 
using namespace std;

const int N = 2e5 + 100;

int n;
int sa[N], dp1[N], dp2[N];
//dp1[i]表示在终点时刻,i能到达的最靠右的位置
//dp2[i]表示i最多能向右走i秒,然后向左折返

void upd(int &a, int b) {
     
	if (a < b) a = b;
}

bool check(int x) {
     
	for (int i = 1; i <= n; i++) dp1[i] = dp2[i] = -2e9;
	dp1[1] = sa[1] + x;
	dp2[1] = x;
	for (int i = 2; i <= n; i++) {
     
		if (dp1[i - 1] < sa[i] - x && sa[i - 1] + dp2[i - 1] < sa[i] - dp2[i - 1]) return false;
		if (dp1[i - 1] >= sa[i] - x) {
     
			if (dp1[i - 1] < sa[i]) upd(dp2[i], (x - (sa[i] - dp1[i - 1])) / 2);
			else {
     
				upd(dp2[i], (x + (dp1[i - 1] - sa[i])) / 2);
				// dp1[i - 1] - (x - t) = sa[i] - t
				int t = (sa[i] + x - dp1[i - 1]) / 2;
				upd(dp1[i], sa[i] + x - 2 * t);
			}
		}
		if (sa[i - 1] + dp2[i - 1] >= sa[i] - dp2[i - 1]) {
     
			//sa[i - 1] + t = sa[i] - t;
			int t = (sa[i] - sa[i - 1]) / 2;
			upd(dp1[i], sa[i] + x - 2 * t);
			upd(dp2[i], (dp2[i - 1] * 2 - (sa[i] - sa[i - 1])) / 2);
		}
	}
	return true;
}

int main() {
     
	//freopen("0.txt", "r", stdin);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", sa + i);
	int l = 1, r = 1e9, m;
	while (l < r) {
     
		m = (l + r) / 2;
		if (check(m)) r = m;
		else l = m + 1;
	}
	printf("%d\n", l);
	return 0;
}

你可能感兴趣的:(动态规划,二分)