二分+贪心的题
二分很简单,主要是怎么贪。我的方法是从如图1号节点开始,尽可能向右贪心,如果max_len超出了,就把项链整体往左移动,这样可以保证利用到的max_len最大,也就是尽可能减少浪费。
由于必须保证每段项链都包含恰好一个美丽宝石,所以项链是无法无限地整体向左移动的。用move记录能向左移动的最大距离,每次分段时都要更新。
最后要判断一下第一段项链和最后一段项链能不能连接上
代码如下
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define go(i, a, b) for (int i = a; i <= b; ++i)
//#define com(i, a, b) for (int i = a; i >= b; --i)
#define mem(a, b) memset(a, b, sizeof(a))
#define fo(i, a) for (int i = 0; i < a; ++i)
const int N = 1e6 + 10;
int n, m, a[N];
bool check(int mlen) {
int move, pos, res = 0;
move = n * 2;
pos = a[1] - 1;
for (int i = 2; i <= m; ++i) {
if (pos + mlen < a[i - 1]) return 0;
int nxtp = a[i] - pos - 1;
if (mlen > nxtp) {
int dis = min(move, mlen - nxtp);
move -= dis;
res += dis;
pos = a[i] - 1;
} else {
pos = pos + mlen;
}
move = min(move, pos - a[i - 1]);
}
if (n - pos + a[1] - 1 - res <= mlen)
return 1;
else
return 0;
}
void read(int &x) {
int f = 1;
char c;
c = getchar();
x = 0;
while (!isdigit(c)) {
if (c == '-') f = -1;
c = getchar();
}
while (isdigit(c)) {
x = x * 10 + c - '0';
c = getchar();
}
x *= f;
}
signed main() {
//freopen("input.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
read(a[i]);
}
int l = 1, r = n;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid))
r = mid - 1;
else
l = mid + 1;
}
cout << r + 1 << endl;
return 0;
}