二分尺取题型总结

尺取 算法 

题型 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距离简单,就不用再去搜比最优解 更小的 距离了。

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

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

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

还在不断完善中。

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

 

 

 

 

 

 

 

 

 

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值