【二分+DP】【YBTOJ】攻击法坛

题目链接:F. 3.攻击法坛

题意

数轴上有(n)个给定的点,(p)个长为(L)(q)个长为(2L)的线段,求出(L)的最小值使得所有点被线段覆盖。

解析

很考验思维的一道题.

发现(L)的可行性有单调性,于是考虑二分(L)
check中:

  • (f1[i])表示,在当前(L)下,从第(i)个点开始可覆盖到第(f1[i])个点;
  • (f2[i])表示,在当前(2L)下,从第(i)个点开始可覆盖到第(f2[i])个点。
    (用(n^2)求出)
    后续使用dp:
  • (dp[i][j])表示使用(i)次第一条线段,(j)次第二条线段,便可得:
    • (dp[i][j] = max{cdots,f1[_{dp[i-1][j]+1}],f2[_{dp[i][j-1]+1}]})
      分别表示从减少一次第一条线段和第二条线段转移而来。

最后,如果达到了第(n)个点即返回。

代码

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 2e3+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,p,q;
int a[N];
int l,r;
int f1[N],f2[N],dp[N][N];
inline bool check(int x){
//	printf("check(%d)
",x);
	memset(f1,0,sizeof(f1)),memset(f2,0,sizeof(f2)),memset(dp,0,sizeof(dp));
	for(int i = 1 ; i <= n ; i ++)
		for(int j = i ; j <= n ; j ++){
			if(1LL * a[i] + x > 1LL * a[j])
				f1[i] = j;
			if(1LL * a[i] + x + x > 1LL * a[j])
				f2[i] = j;
		}
	for(int i = 0 ; i <= p ; i ++)
		for(int j = 0 ; j <= q ; j ++){
			if(i)
				dp[i][j] = max(dp[i][j],f1[dp[i-1][j]+1]);
			if(j)
				dp[i][j] = max(dp[i][j],f2[dp[i][j-1]+1]);
			if(dp[i][j] >= n)return 1;
		}
	return dp[p][q] >= n;
}
signed main(){
	n = read() , p = read() , q = read();
	for(int i = 1 ; i <= n ; i ++)
		a[i] = read();
	sort(a+1,a+n+1);
	l = 1 , r = a[n]-a[1]+1;
	while(l < r){
		int mid = (l + r) >> 1;
		if(check(mid))r = mid;
		else l = mid + 1;
	}
	printf("%d",l);
	return 0;
}
原文地址:https://www.cnblogs.com/Shinomiya/p/15032548.html