1. 程式人生 > >二分尺取題型總結

二分尺取題型總結

尺取 演算法 

題型 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距離簡單,就不用再去搜比最優解 更小的 距離了。

這時候就需要去遍歷距離,從而得到那個最優解,如果暴力遍歷,資料量過大,很容易超時,我們 就用 二分的方法來做,就像(二分查詢逐個分一半,在一半中遍歷,就不用全部遍歷,達到優化的效果,大大縮短時間),我們就可以把答案二分,得到最優的答案。

是如何得到最優的答案,如果目前的距離可以裝下所有的牛,就將距離拉長,如果不滿足,就將距離縮短,我們 一開始 定義了 兩頭牛之間的最短距離和最長距離,我們 就可以分割槽間 ,在哪一個區間 滿足,就在 哪個區間 繼續分 區間,如果在分的時候,不滿足條件了 ,就會 往回退(意思就是將距離變小一點),就這樣,一直一直分,直到不能再分的時候,就停止繼續二分,這樣就 能得到 牛的最小距離的最大值。

上面 有個 尺取 ,排版 有點亂 ,抱歉,我在完善完善。

還在不斷完善中。

如有錯誤 ,請指正,謝謝。