二分—进击的奶牛、寻找段落

进击的奶牛

题目描述

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;
}
posted @ 2023-07-16 01:49  LongDz  阅读(34)  评论(0)    收藏  举报