二分—进击的奶牛、寻找段落
进击的奶牛
题目描述
Farmer John 建造了一个有 \(N\)(\(2 \leq N \leq 10 ^ 5\)) 个隔间的牛棚,这些隔间分布在一条直线上,坐标是 \(x _ 1, x _ 2, \cdots, x _ N\)(\(0 \leq x _ i \leq 10 ^ 9\))。
他的 \(C\)(\(2 \leq C \leq N\))头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John 想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?
输入格式
第 \(1\) 行:两个用空格隔开的数字 \(N\) 和 \(C\)。
第 \(2 \sim N+1\) 行:每行一个整数,表示每个隔间的坐标。
输出格式
输出只有一行,即相邻两头牛最大的最近距离。
样例 #1
样例输入 #1
5 3
1
2
8
4
9
样例输出 #1
3
最近刚学了一下二分,这道题就是二分里的最小值最大化问题。如果用常规思路:暴力法来做的话,就是利用贪心从小到大枚举最小距离值dis,时间复杂度为O(nc),注意第一头牛的位置一定是放在x1上的,因为题目要求为最大值。
回归真题,分析dis取值过程中可以发现,dis是递增的,同时有上下界,所以比较适合用二分法来做,时间复杂度为O(nlog2n)。
代码实现
`#include <bits/stdc++.h>`
`using namespace std;`
`const int N =1e5+5;`
`int a[N];`
`int n,c;`
`bool check(int dis)`
`{`
`int pre=0;int count=1;`
`for(int i=0;i<n;i++)`
`{`
`if(a[i]-a[pre]>=dis)`
`{`
`pre=i;`
`count++;`
`}`
`}`
`if(count>=c)`
`return true;`
`else return false;`
`}`
`int main()`
`{`
`cin>>n>>c;`
`for(int i=0;i<n;i++)cin>>a[i];`
`sort(a,a+n);`
`int right=a[n-1]-a[0],left=0;`
`int ans;`
`while (left < right)`
`{`
`int mid=left+right>>1;`
`if (check(mid)){`
`ans=mid;left=mid+1;`
`}`
`else right=mid;`
`}`
`cout<<ans;`
`return 0;`
`}`
寻找段落
题目描述
给定一个长度为 \(n\) 的序列 \(a\),定义 \(a_i\) 为第 \(i\) 个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在 \([S, T]\) 之间的连续序列。最有价值段落是指平均值最大的段落。
段落的平均值 等于 段落总价值 除以 段落长度。
输入格式
第一行一个整数 \(n\),表示序列长度。
第二行两个整数 \(S\) 和 \(T\),表示段落长度的范围,在 \([S, T]\) 之间。
第三行到第 \(n+2\) 行,每行一个整数表示每个元素的价值指数。
输出格式
一个实数,保留 \(3\) 位小数,表示最优段落的平均值。
样例 #1
样例输入 #1
3
2 2
3
-1
2
样例输出 #1
1.000
提示
【数据范围】
对于 \(30\%\) 的数据有 \(n \le 1000\)。
对于 \(100\%\) 的数据有 \(1 \le n \le 100000\),\(1 \le S \le T \le n\),\(-{10}^4 \le a_i \le {10}^4\)。
第一眼看到题干的时候,不禁想起求最大子序和问题,即利用前辍和+单调队列解决问题。但题目要求需要计算的是平均值最大的段落,所以首先转换问题,将求最大平均变为求最大和,脑子没转过款,看了别人题解才意识到,先假设一个平均值,将“各元素价值”-“平均值”,再求和,即可完成转换。这里假设一个平均值,就可以自然而然的用二分去处理,有单调性且有界。本题的难点在于二分法check函数的编写,由于考虑到“滑动窗口”的窗口大小是有范围的,所以需要在仔细考虑窗口最小处的情况。额,这个目前还是有点迷糊。
以下给出代码实现
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define maxn 100010
using namespace std;
int n, s, t, i;
double l, r, mid, ans;
double a[maxn];
int b[maxn];
double sum[maxn];
int q[maxn];
bool check(double x) {
int i, l = 1, r = 0;
for (i = 1; i <= n; i++)
a[i] = (double)b[i] - x;
sum[0] = 0;
for (i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
for (i = 1; i <= n; i++) {
if (i >= s) {
while (r >= l && sum[i - s] < sum[q[r]]) r--;
q[++r] = i - s;
}
if (l <= r && q[l] < i - t) l++;
if (l <= r && sum[i] - sum[q[l]] >= 0) return true;
}
return false;
}
int main() {
scanf("%d", &n);
scanf("%d%d", &s, &t);
for (i = 1; i <= n; i++)
scanf("%d", &b[i]);
ans = l = -10000; r = 10000;
while (r - l > 1e-5) {
mid = (l + r) / 2;
if (check(mid))
ans = l = mid;
else r = mid;
}
printf("%.3lf\n", ans);
return 0;
}

浙公网安备 33010602011771号