https://codeforces.com/gym/102279/problem/C
题意
扫雷,在长度为 lenght 的街道上有 n 个地雷,再输入每个地雷在街道上的位置,1<=lenght<=1e9,1<=n<=2000;
现有排雷范围为w,2w的一次性排雷装置,1<=w,分别由p、q个。现问w的值至少为多大。
思路
先二分找w,对于每次二分的值写一个函数进行判断。用dp来判断,设dp式
d[i][j]=res,i表示排除前i个地雷用了j个w型和res个2w型的排雷装置。
题解
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2001;
int d[maxn][maxn], n, p, q, a[maxn], inf = 1e9 + 1;
int q1[maxn], q2[maxn];
void up(int &x, int y) {
x = min(x, y);
}
int ok(int i, int j, int w) {
if (a[j] - a[i] + 1 > w)
return 0;
return 1;
}
int gao(int w) {
if (1ll * w * p + 2ll * w * q >= a[n] - a[1] + 1)
return 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= min(i, p); j++)
d[i][j] = inf;
int t1 = 0, h1 = 0, t2 = 0, h2 = 0;
q1[t1++] = 1;
q2[t2++] = 1;
d[1][0] = 1;
d[1][1] = 0;
for (int i = 2; i <= n; i++) {
while (h1 < t1 && !ok(q1[h1], i, w)) //找到w覆盖点的范围
h1++;
q1[t1++] = i;
int flag = inf;
for (int j = 0; j < p; j++)
if (d[q1[h1] - 1][j] <= q) {
up(d[i][j + 1], d[q1[h1] - 1][j]); //w型dp转移式
up(flag, d[i][j + 1]);
}
while (h2 < t2 && !ok(q2[h2], i, 2 * w)) //找到2w覆盖点的范围
h2++;
q2[t2++] = i;
for (int j = 0; j <= p; j++)
if (d[q2[h2] - 1][j] < q) {
up(d[i][j], d[q2[h2] - 1][j] + 1); //2w型dp转移式
up(flag, d[i][j]);
}
if (flag == inf)
return 0;
}
return 1;
}
int main() {
cin>>n>>p>>q;
p = min(p, n);
q = min(q, n);
for (int i = 1; i <= n; i++)
cin>>a[i];
sort(a + 1, a + 1 + n);
int l = 1, r = 1e9;
while (l < r) { //二分答案
int m = (l + r) / 2;
if (gao(m))
r = m;
else
l = m + 1;
}
cout<<l;
}