二分尺取題型總結
尺取 演算法
題型 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距離簡單,就不用再去搜比最優解 更小的 距離了。
這時候就需要去遍歷距離,從而得到那個最優解,如果暴力遍歷,資料量過大,很容易超時,我們 就用 二分的方法來做,就像(二分查詢逐個分一半,在一半中遍歷,就不用全部遍歷,達到優化的效果,大大縮短時間),我們就可以把答案二分,得到最優的答案。
是如何得到最優的答案,如果目前的距離可以裝下所有的牛,就將距離拉長,如果不滿足,就將距離縮短,我們 一開始 定義了 兩頭牛之間的最短距離和最長距離,我們 就可以分割槽間 ,在哪一個區間 滿足,就在 哪個區間 繼續分 區間,如果在分的時候,不滿足條件了 ,就會 往回退(意思就是將距離變小一點),就這樣,一直一直分,直到不能再分的時候,就停止繼續二分,這樣就 能得到 牛的最小距離的最大值。
上面 有個 尺取 ,排版 有點亂 ,抱歉,我在完善完善。
還在不斷完善中。
如有錯誤 ,請指正,謝謝。