Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

JOI Open 2013 - Watching

問題文 : ウォッチング

解法 (ただし人と違う解き方をしているらしい) :
50 点分の解法は, dp[i][j][k] : 位置i までを小カメラj 個, 大カメラk 個で覆うときの距離の最小値, として, O(N^4) で計算できる. (二分探索とは概念)

これを高速化するために, dp[i][j] :距離w が固定されているとき, 小カメラi 個と大カメラj 個で覆えるイベントの最大値とすると, O(N^2 * logN) までこれを高速化することができる. w は二分探索で定めればよい為, O(N^2 * 30 * logN) となるが, 制限時間が2 sec あるのでこれで十分である.

普通はdp[i][j] : i 番目までをj 個の大カメラで覆うときに必要な小カメラの最小値, とするらしいですが, なんでもよさそうです.

コード :

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int A[2013];
int dp[2048][2048];
int N, _P, _Q;
int lim;

bool ok()
{
	
	for (int i = 0; i <= _P; i++){
		for (int j = 0; j <= _Q; j++){
			dp[i + 1][j] = max(dp[i + 1][j], (int)(upper_bound(A, A + N, A[dp[i][j] + 1] + lim - 1) - A - 1));
			dp[i][j + 1] = max(dp[i][j + 1], (int)(upper_bound(A, A + N, A[dp[i][j] + 1] + lim * 2 - 1) - A - 1));
			//printf("(%d, %d) = %d / (%d, %d) = %d\n", i + 1, j, dp[i + 1][j], i, j + 1, dp[i][j + 1]);
			if ((i + 1 <= _P && dp[i + 1][j] == N - 1) || (j + 1 <= _Q && dp[i][j + 1] == N - 1)) return (true);
		}
	}
	
	return (false);
}



int main()
{
	scanf("%d %d %d", &N, &_P, &_Q);
	
	if (_P + _Q >= N) return (!printf("1\n"));
	
	for (int i = 0; i < N; i++)
		scanf("%d", A + i);
	
	sort(A, A + N);
	int lo = 1, hi = 1000000000;
	while (lo != hi){
		int mid = (lo + hi) / 2;
		lim = mid;
		
		memset(dp, -1, sizeof(dp));
		
		if (ok()) hi = mid;
		else lo = mid + 1;
	}
	
	printf("%d\n", lo);
	
	return (0);
}