二分尺取题型总结

尺取 算法 

题型 1 

查找序列的连续元素的子序列的最小长度,其总和大于或等于 maxsum。

过程分为四步

1.初始化左右端点。

2.不断扩大右端点,直到不满足while(sum<=maxsum&&r<n)的条件 。

3.如果sum 小于 maxsum ,直接结束(上一个 while 结束时 肯定遍历到了右边界)。或者sum 大于 maxsum ,进行下一步。

4.不断地将左端点 往右撤,一步一步的判断减去后是否比summax小,如果大,sum 的和 也相应地减去 a[j++] ,逐渐回到初始状态。如果 在中间减得过程有 sum小于 summax 的情况,那就 不好意思 ,回退过程结束,进行往右扩展,就会一直这样扩展,判断,回退,再扩,直到 到了 右边界 的时候 ,退出,while(1)。

用了一个 while(1) break;  来不断 的 实现 求出 最短的 序列 ,上面 实现过程中 ,都会有一次  ans==min(r-1,ans)  的结果 更新 。

算法相当 精妙。

模板::

{

int l=0,r=0,ans=n+1;
while(1)
{
    while(sum<=maxsum&&r<n)
          sum=sum+a[r++];  

    if(sum<maxsum)
        break;

    ans=min(r-1,ans);

    sum=sum-a[l++];
    
}

    if(ans>n)
        cout<<"no"<<endl;

    cout<<ans<<endl;


return 0;

}

2.找出一串数字 的 最大 的 和,并找出起始下表和末尾下标。

也是尺取的思想 ,不断对 左端点 和右端点 进行 移动 ,达到最大 的 一个 子序列。首先有一个 maxsum  来记录现在状态的和,还需要一个 去前面探路的 sum ,

#include<cstdio>
#include<cstring>
#include<iostream> 
#include<algorithm>
const int maxn=100005;
using namespace std;
typedef long long ll;
int a[maxn];
int main()
{
	int t,n,i,start,end,summax,sum,kase=0,g=0;
	cin>>t;
	while(t--)
	{
		memset(a,0,sizeof(a)); 
		sum=0;
		summax=0;
		cin>>n;
		for(i=0;i<n;i++)
		{
			cin>>a[i];
		}
		summax=a[0];
		start=0;
		end=0;
		int l=0;
		for(i=0;i<n;i++)
			{
				sum=sum+a[i];
				if(sum>summax)
				{
					summax=sum;
					end=i;
					l=start;
				}
				if(sum<0)
				{
					sum=0;
					start=i+1;	
				}
			}
			if(g)
				cout<<endl;
			printf("Case %d:\n",++kase);
		cout<<summax<<' '<<l+1<<' '<<end+1<<endl;
				g=1;		
	}	
	return 0;
}

3.题面:

Aggressive cows

农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 <= xi <= 1,000,000,000). 但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?

Input

有多组测试数据,以EOF结束。 第一行:空格分隔的两个整数N和C 第二行——第N+1行:分别指出了xi的位置

Output

每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。

Sample Input

5 3
1
2
8
4
9

Sample Output

3

Hint

1位置放一头牛,4位置放一头牛,它们的差值为3;最后一头牛放在8或9位置都可以,和4位置的差值分别为4、5,和1位置的差值分别为7和8,不比3小,所以最大的最小值为3。

本题 是一个 二分答案 ,尺取 ,的题型。

代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#include<string.h>
typedef long long ll;
const ll maxn=100005; 
ll a[maxn]; 
ll n;
ll cownum;

bool check(ll x)    //判断这个 mid是大了还是小了,小了不满足题意,大了 装不下牛 
{
	ll k=1;
	ll i,niunow;
	niunow=a[0];
	for(i=1;i<n;i++)
		if(a[i]-niunow>=x)
		{
			niunow=a[i]; 
			k++;
		if(k==cownum)
			return true;
		}
		return false;
}
int main()
{
	ll i;
	while(scanf("%lld%lld",&n,&cownum)!=EOF)   //cin cout 超时  
	{
		ll mid;
		memset(a,0,sizeof(a));
		for(i=0;i<n;i++)
			scanf("%lld",&a[i]);
		//	cin>>a[i];          //会超时  
		sort(a,a+n);
		ll left=0,right=a[n-1]-a[0];
		while(left<=right)            
		{
			mid=(left+right)/2.0;
			if(check(mid))
				left=mid+1;	     //  往距离大的 答案去找 
			else
				right=mid-1;	 // 往距离小的答案去找 
		}
		printf("%lld\n",left-1); 		
		//cout<<left-1<<endl;    //用 cin cout 会超时  
		
		
	}
	return 0;
}

什么是二分答案呢 ?

就是对答案进行一系列的查找,直到找到那个 最优的答案就可以,感觉和二分查找类似,大多数情况下用于求解满足某种条件的最大(最小)值。下面看个图

在判断牛是否能装下时,用了一个 贪心 算法,使更多的牛能装下。先从小的隔间里 装牛,可以的话,就把这个牛的隔间位置记录,然后再比较下一个牛与它之间的距离,如果 大于之前 mid 二分的距离,就能装,如果不行,就不能装,直到所给的牛,全装下(或者不能装下),再去二分。贪心。

就像这个题一样,两头牛之间的距离就是这个 代价一样,如果太小,放在一个隔间里面,就不满足条件,如果让它们距离够大(前提是要把指定的牛装满),也不满足条件,所以牛隔间之间距离不大也不小(可以装下所有的牛,也不至于两头牛在一个隔间里面),才可以满足条件,然后再求满足条件中任意两头牛之间最小距离的最大值。

首先先想,两牛之间的最大距离是什么,首先对牛的隔间序号排序,那么第一个隔板和最后一个隔板间的距离就是两头牛最大的距离,这样 从 最大的距离 开始去分区间 ,来做,比从1距离简单,就不用再去搜比最优解 更小的 距离了。

这时候就需要去遍历距离,从而得到那个最优解,如果暴力遍历,数据量过大,很容易超时,我们 就用 二分的方法来做,就像(二分查找逐个分一半,在一半中遍历,就不用全部遍历,达到优化的效果,大大缩短时间),我们就可以把答案二分,得到最优的答案。

是如何得到最优的答案,如果目前的距离可以装下所有的牛,就将距离拉长,如果不满足,就将距离缩短,我们 一开始 定义了 两头牛之间的最短距离和最长距离,我们 就可以分区间 ,在哪一个区间 满足,就在 哪个区间 继续分 区间,如果在分的时候,不满足条件了 ,就会 往回退(意思就是将距离变小一点),就这样,一直一直分,直到不能再分的时候,就停止继续二分,这样就 能得到 牛的最小距离的最大值。

上面 有个 尺取 ,排版 有点乱 ,抱歉,我在完善完善。

还在不断完善中。

如有错误 ,请指正,谢谢。

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/84900800