题目:数列分段
题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:例如一数列4 2 4 5 1要分成3段将其如下分段:[4 2][4
5][1]第一段和为6,第2段和为9,第3段和为1,和最大值为9。将其如下分段:[4][2 4][5
1]第一段和为4,第2段和为6,第3段和为6,和最大值为6。并且无论如何分段,最大值不会小于6。所以可以得到要将数列4 2 4 5
1要分成3段,每段和的最大值最小为6。输入 输入文件divide_b.in的第1行包含两个正整数N,M,第2行包含N个空格隔开的非负整数A[i],含义如题目所述。
输出 输出文件divide_b.out仅包含一个正整数,即每段和最大值最小为多少。 样例输入 5 3 4 2 4 5 1
样例输出 6 题目说明 对于20%的数据,有N≤10;
对于40%的数据,有N≤1000;
对于100%的数据,有N≤100000,M≤N, A[i]之和不超过10^9。
题目思路
这是一道找最大值里的最小值的题目,很显然是用二分法求值。通过枚举答案来解。重点是如何写check函数,我们知道分段都是连续的,我们可以枚举答案的时候,利用尝试的答案来将数列分段,那么分段的结果就有分成了大于M段,或者小于等于M段。
check函数的思想就是拿这个mid值来枚举分段,在保证每一段不会大于mid值,来看看会分成几段,因为mid值这里看作段的最大值,如果分的段数小于输入给的段数,说明这个mid还不是段最大值中的最小值,继续缩小范围,再找最小
举例代码实现过程:
5 3
4 2 4 5 1
left=5,right=16 mid=10(每一段的最大值肯定大于单个数,并且是最大值,所以肯定大于最大的数,但每一段不可能大于所有的总和,所以right等于总和)
mid=10传入check函数,4+2+4为第一段,因为加上下一个,sum就大于10了,然后cnt++,sum变成现在的5,5再加下一个1,还是没有大于10,所以cnt小于m(3) 所以return false
说明mid这个数找的太大了,然后变成(5,10),mid=7
mid=7传入check函数,4+2后sum为6没有大于7,但是加上下一个4,sum大于7,所以cnt++,sum变成了现在的4,加上下一个5后,又大于了7,所以cnt++,sum变成现在的5,加上下一个1。所以cnt=2,还是false
下一个数是mid=6 同样的道理cnt=3,还是false,right=6;
然后下一个mid=5,返回true,left=6
跳出了循环,输出right=6;
code:
#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <cstdio>
#include <vector>
#include <algorithm>
#define LL long long
#define INF 99999999
using namespace std;
int n, m;
int l, r, mid;
int a[100005];
bool check(int x){
int sum = 0;
int cnt = 1;
for(int i = 0; i < n; i++){
sum += a[i];
if(sum > x){
cnt++;
sum = a[i];
}
}
if(cnt > m)
return true;
else
return false;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 0; i < n; i++){
scanf("%d",&a[i]);
r += a[i];
l = max(l,a[i]);
}
while(l < r){
mid = (l+r) >> 1;
if(check(mid))
l = mid + 1;
else
r = mid;
}
printf("%d\n",r);
return 0;
}
变型题:
对于给定的一个长度为N的正整数数列Ai,现要将其分成连续的若干段,并且每段和不超过M(可以等于M),问最少能将其分成多少段使得满足要求。
Input 第1行包含两个正整数N,M,表示了数列Ai的长度与每段和的最大值,第2行包含N个空格隔开的非负整数Ai,如题目所述。
Output 一个正整数,输出最少划分的段数。
Samples
Input
5 6 4 2 4 5 1
Output
3
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e9;
int n;
int m;
int a[100005];
int sum;
int ans;
ll read() {
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void out(ll x) {
int stackk[40];
if (x < 0) {
putchar('-');
x = -x;
}
if (!x) {
putchar('0');
return;
}
int top = 0;
while (x) stackk[++top] = x % 10, x /= 10;
while (top) putchar(stackk[top--] + '0');
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i++)
{
if(sum+a[i]<=m){
sum+=a[i];
}else{
ans++;
sum=a[i];
}
if(i==n&&sum<=m){
ans++;
}
}
out(ans);
return 0;
}
题目:疯牛问题
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0
<= xi <= 1,000,000,000). 但是,John的C (2 <= C <=
N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入 有多组测试数据,以EOF结束。 第一行:空格分隔的两个整数N和C 第二行——第N+1行:分别指出了xi的位置 输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1 2 8 4 9
样例输出
3
题意:简单的说就是给你一段长度,在这一段中给出m个点,然后在这m个点中选出c个点,让这c个点之间相邻两个点的之间距离的最小值最大
思路:通过二分枚举这个最小值,然后通过贪心的思想找出满足要求的最大的这个最小值
judge函数就是以段找,如果找到了一段大于等于我传入的最小值,cnt++,但是保证这一段距离是小于mid最小值的,如果最后分出来的段数是大于输入给的c个点,说明可以继续增加试探,mid变大,因为这还不是最小值中的最大值。
code:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n, c;
int pos[100005];
bool judge(int k)
{
int cnt = 1;
int st = pos[0];
for(int i = 1; i < n; ++i)
{
if(pos[i] - st >= k)
{
++cnt;
if(cnt >= c)
return true;
st = pos[i];
}
}
return false;
}
int binary_search() // 二分枚举满足条件的最大距离
{
int left = 0;
int right = pos[n-1] - pos[0];
int mid = (left + right) >> 1;
while(left <= right)
{
if(judge(mid)) // 所求距离 >= mid,可以继续增大试探
left = mid+1;
else // 所求距离 < mid,所以必须减小来试探
right = mid-1;
mid = (left + right) >> 1;
}
return left-1;
}
int main()
{
while(~scanf("%d%d", &n, &c))
{
for(int i = 0; i < n; ++i)
scanf("%d", &pos[i]);
sort(pos, pos+n);
printf("%d\n", binary_search());
}
return 0;
}
题目描述:(火车站)
输入
2 2
4 106
输出
34
code:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long a[MAXN];
int ll,n,k;
long long judge(long long mid)
{
int m=0;
for(int i=2;i<=n;i++)
if(a[i]-a[i-1]>=mid)
{
m+=(a[i]-a[i-1])/mid;
if((a[i]-a[i-1])%mid==0)
m--;
}
if(m>k)
return 0;
return 1;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
long long l=0;
long long r=1000000000000;
while(l<r)
{
long long mid=(l+r)/2;
if(judge(mid)==1)
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}