填坑 回溯 預習 之 二分 尺取大總結

2021-10-02 14:56:39 字數 3217 閱讀 6949

尺取法

尺取法通常是對陣列儲存一對下標,即所選取的區間的左右端點,然後根據實際情況不斷地推進區間左右端點以得出答案。尺取法比直接暴力列舉區間效率高很多,尤其是資料量大的時候,所以說尺取法是一種高效的列舉區間的方法,是一種技巧,一般用於求取有一定限制的區間個數或最短的區間等等。當然任何技巧都存在其不足的地方,有些情況下尺取法不可行,無法得出正確答案,所以要先判斷是否可以使用尺取法再進行計算。

使用尺取法時應清楚以下四點:

1、 什麼情況下能使用尺取法? 2、何時推進區間的端點? 3、如何推進區間的端點? 4、何時結束區間的列舉?

尺取法通常適用於選取區間有一定規律,或者說所選取的區間有一定的變化趨勢的情況,通俗地說,在對所選取區間進行判斷之後,我們可以明確如何進一步有方向地推進區間端點以求解滿足條件的區間,如果已經判斷了目前所選取的區間,但卻無法確定所要求解的區間如何進一步得到根據其端點得到,那麼尺取法便是不可行的。首先,明確題目所需要求解的量之後,區間左右端點一般從最整個陣列的起點開始,之後判斷區間是否符合條件在根據實際情況變化區間的端點求解答案。

例子:題意:給定乙個序列,使得其和大於或等於s,求最短的子串行長度。

思路:序列都是正數,如果乙個區間和大於等於s,那麼就不需要往後再推進,因為其和也比大於s,序列還長了,所以這個時候左端點移動以進一步找到最短的區間,如果右端點到了區間末尾其和還不大於等於s,結束區間的列舉。

#include

#define maxn 100005

#define inf 0x3f3f3f3f

using

namespace std;

typedef

long

long ll;

ll a[maxn]

;int

main()

if(ans = inf) ans =0;

printf

("%d\n"

,ans);}

return0;

}

例子2

一本書有 p 頁,每頁都有個知識點a[i],知識點可能重複,求包含所有知識點的最少的頁數。

思路:跟上乙個題差不多,從頭開始如果乙個子區間滿足條件,那麼區間推進到該處時,右端點固定,左端點向右移動以求得到最短的子區間。只是需要儲存所在區間知識點的數量,那麼使用map進行對映以快速的判斷是否所選取的頁數是否覆蓋了所有的知識點

#include

#define inf 0x3f3f3f3f

using

namespace std;

const

int maxn =

1000010

;int a[maxn]

;map<

int,

int> hashtable;

//用於儲存已出現的知識點以及其出現的次數

set<

int> temp;

intmain()

int len = temp.

size()

;//set容器自動有序並去重,可以利用這一性質來得到不同的知識點總數量

temp.

clear()

;int left =

1, right =

1, num =

0, ans = inf;

while(1

)if(num < len)

// 此分支有兩個用處,一是若上面那個迴圈完成後知識點總數不足要求,就退出迴圈(這個題不需要)。二是求最大左端點

break

;

ans =

min(ans, right - left)

;//【left,right) 區間左閉右開if(

--hashtable[a[left++]]

==0)//如果去掉最右邊元素,不同的

--num;

}printf

("%d\n"

,ans)

;return0;

}

例子3

題意:給定乙個陣列和乙個值t,求乙個子區間使得其和的絕對值與t的差值最小,如果存在多個,任意解都可行。

#include

#include

#include

#define inf 0x3f3f3f3f

#define ll long long

#define max 100010

using

namespace std;

typedef pairint> p;

ll a[max]

, t, ans, tmp, b;

int n, k, l, u, st, en;

p sum[max];

ll myabs

(ll x)

intmain()

sort

(sum, sum+

1+n)

;while

(k--)if

(b > t) st++

;else

if(b < t) en++

;else

break;if

(st == en) en++;}

if(u < l)

swap

(u, l)

;printf

("%i64d %d %d\n"

, ans, l+

1, u);}

}return0;

}

題意:找到某乙個區間使得區間內的數的和/平方和等於某一給定值k。

#include

#include

#include

#include

#include

#include

#define inf 0x3f3f3f3f

#define ll long long

using

namespace std;

int prime=

;int

main()

}printf

("%d\n"

, ans);}

}

總結:尺取法的模型便是這樣:根據區間的特徵交替推進左右端點求解問題,其高效的原因在於避免了大量的無效列舉,其區間列舉都是根據區間特徵有方向的列舉,如果胡亂使用尺取法的話會使得列舉量減少,因而很大可能會錯誤,所以關鍵的一步是進行問題的分析!

尺取 二分查詢

尺取法 這是一種比較有趣的方法,想吃子一樣去解決問題。現在我只是知道了可以用陣列來模擬 尺子 加油學習!方法是 陣列模擬 二分查詢 說一 下資料的意思,10個數,從中找出和為15的最短子串。10 5 1 3 5 10 7 4 9 2 8 15 include using namespace std ...

K th Number(尺取 二分)

傳送門 釐清題意很重要,給你乙個序列a,再給出k,m,將a中每個區間的第k大元素放入b序列,問b序列的第m大元素是多少。思路 可以想到一段序列的所有區間數是固定的,每個區間的第k大也都是固定的。假設b序列的第m大元素是x,則可知大於等於x的數有m個,即對於a序列的所有區間第k大,大於x的有m個。這裡...

HDU 6231 (二分 尺取)

所求的一定是a陣列中的原素,這點毋庸置疑,所以在a陣列中任意選則其中的乙個數x,若a的所有長度不小於k的連續子串行中第k大數不小於x的子串行一共有ans個,那麼x在所有第k大元素組成的數列中至少是第ans大數 因為ans n r,因為之前的x已經是第k大了,所以加上後面的元素,如果比他小,則沒有影響...