從擺石頭和牛舍問題看二分

2022-08-17 04:21:10 字數 4515 閱讀 6712

一年一度的「跳石頭」比賽又要開始了!

這項比賽將在一條筆直的河道中進行,河道中分布著一些巨大岩石。組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 nnn 塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。

為了提高比賽難度,組委會計畫移走一些岩石,使得選手們在比賽過程中的最短跳躍距離盡可能長。由於預算限制,組委會至多從起點和終點之間移走 mmm 塊岩石(不能移走起點和終點的岩石)。

輸入格式:

第一行包含三個整數 l,n,ml,n,ml,n,m,分別表示起點到終點的距離,起點和終點之間的岩石數,以及組委會至多移走的岩石數。保證 l≥1l \geq 1l≥1 且 n≥m≥0n \geq m \geq 0n≥m≥0。

接下來 nnn 行,每行乙個整數,第 iii 行的整數 di(0di​(0輸出格式:

乙個整數,即最短跳躍距離的最大值。

輸入樣例#1:複製

25 5 2 211

1417

21

輸出樣例#1:複製

4

輸入輸出樣例 1 說明:將與起點距離為 222和 141414 的兩個岩石移走後,最短的跳躍距離為 444(從與起點距離 171717 的岩石跳到距離 212121 的岩石,或者從距離 212121 的岩石跳到終點)。

另:對於 20%20\%20%的資料,0≤m≤n≤100 ≤ m ≤ n ≤ 100≤m≤n≤10。

對於50%50\%50%的資料,0≤m≤n≤1000 ≤ m ≤ n ≤ 1000≤m≤n≤100。

對於 100%100\%100%的資料,0≤m≤n≤50,000,1≤l≤1,000,000,0000 ≤ m ≤ n ≤ 50,000,1 ≤ l ≤ 1,000,000,0000≤m≤n≤50,000,1≤l≤1,000,000,000

附上0分**

#include

#define for(i,l,r) for(int i=l;i<=r;i++)

#define inf 0x7f7f

using namespace std;

long long ll,n,m,a[inf],mid,ans,l,r;

//首先是可行,其次考慮最優

bool check(int x)

a[n+1]=ll;

l=1,r=ll;

while(l<=r)

else r=mid-1;

}cout<

附上滿分**

#include

#define for(i,l,r) for(int i=l;i<=r;i++)

#define inf 0x7f7f7f

using namespace std;

long long ll,n,m,a[inf],mid,ans,l,r;

//首先是可行,其次考慮最優

bool check(int x)

a[n+1]=ll;

l=1,r=ll;

while(l<=r)

else r=mid-1;//這些可以根據自己理解寫

}cout<

看看有什麼不同

是不是很坑

好了回歸主題

二分就是乙個從可行性問題到最優性問題

關鍵是看她可行解是在什麼範圍,可行解向最優解過度是左移還是右移

二分答案應該是在乙個單調閉區間上進行的。也就是說,二分答案最後得到的答案應該是乙個確定值,而不是像搜尋那樣會出現多解。二分一般用來解決最優解問題。剛才我們說單調性,那麼這個單調性應該體現在**呢?

可以這樣想,在乙個區間上,有很多數,這些數可能是我們這些問題的解,換句話說,這裡有很多不合法的解,也有很多合法的解。我們只考慮合法解,並稱之為可行解。考慮所有可行解,我們肯定是要從這些可行解中找到乙個最好的作為我們的答案, 這個答案我們稱之為最優解。

最優解一定可行,但可行解不一定最優。我們假設整個序列具有單調性,且乙個數x為可行解,那麼一般的,所有的x'(x'y)都是非法解。

那麼什麼時候適用二分答案呢?注意到題面:使得選手們在比賽過程中的最短跳躍距離盡可能長。如果題目規定了有「最大值最小」或者「最小值最大」的東西,那麼這個東西應該就滿足二分答案的有界性(顯然)和單調性(能看出來)。

那就好辦了。我們二分跳躍距離,然後把這個跳躍距離「認為」是最短的跳躍距離,然後去以這個距離為標準移石頭。使用乙個judge判斷這個解是不是可行解。如果這個解是可行解,那麼有可能會有比這更優的解,那麼我們就去它的右邊二分。為什麼去右邊?答案是,這個區間是遞增的 ,而我們求的是最短跳躍距離的最大值,顯然再右邊的值肯定比左邊大,那麼我們就有可能找到比這更優的解,直到找不到,那麼最後找到的解就有理由認為是區間內最優解。反過來,如果二分到的這個解是乙個非法解,我們就不可能再去右邊找了。因為性質,右邊的解一定全都是非法解。那麼我們就應該去左邊找解。整個過程看起來很像遞迴,實際上,這個過程可以遞迴寫, 也可以寫成非遞迴形式,我個人比較喜歡使用非遞迴形式。

下乙個問題,這個judge怎麼實現呢?judge函式每個題有每個題的寫法,但大體上的思想應該都是一樣的——想辦法檢測這個解是不是合法。拿這個題來說,我們去判斷如果以這個距離為最短跳躍距離需要移走多少塊石頭,先不必考慮限制移走多少塊,等全部拿完再把拿走的數量和限制進行比對,如果超出限制,那麼這就是乙個非法解,反之就是乙個合法解,很好理解吧。

可以去模擬這個跳石頭的過程。開始你在i(i=0)位置,我在跳下一步的時候去判斷我這個當前跳躍的距離,如果這個跳躍距離比二分出來的mid小,那這就是乙個不合法的石頭,應該移走。為什麼?我們二分的是最短跳躍距離,已經是最短了,如果跳躍距離比最短更短豈不是顯然不合法,是這樣的吧。移走之後要怎麼做?先把計數器加上1,再考慮向前跳啊。去看移走之後的下一塊石頭,再次判斷跳過去的距離,如果這次的跳躍距離比最短的長,那麼這樣跳是完全可以的,我們就跳過去,繼續判斷,如果跳過去的距離不合法就再拿走,這樣不斷進行這個操作,直到i = n+1,為啥是n+1?河中間有n塊石頭,顯然終點在n+1處。(這裡千萬要注意不要把n認為是終點,實際上從n還要跳一步才能到終點)。

模擬完這個過程,我們檢視計數器的值,這個值代表的含義是我們以mid作為答案需要移走的石頭數量,然後判斷這個數量 是不是超了就行。如果超了就返回false,不超就返回true。//以上皆為**

牛舍#include

#define for(i,l,r) for(int i=l;i<=r;++i)

using namespace std;

int n,m,a[101010],r,l,mid;

bool check(int x)

return cow==m?1:0;

}int main()

sort(a+1,a+n+1);

l=0,r=a[n]-a[1];

doelse

r=mid-1;

}while(l<=r);

cout陶陶是個貪玩的孩子,他在地上丟了a個瓶蓋,為了簡化問題,我們可以當作這a個瓶蓋丟在一條直線上,現在他想從這些瓶蓋裡找出b個,使得距離最近的2個距離最大,他想知道,最大可以到多少呢?

輸入格式:

第一行,兩個整數,a,b。(b<=a<=100000)

第二行,a個整數,分別為這a個瓶蓋座標。

輸出格式:

僅乙個整數,為所求答案。

輸入樣例#1:複製

5 3

1 2 3 4 5

輸出樣例#1:複製

2

限時3秒

0分**

#include

#define for(i,l,r) for(int i=l;i<=r;++i)

using namespace std;

long long n,m,a[101010],r,l,mid,ans,ct=1,now=1;

bool check(int x)

return ct>=m?1:0;

}int main()

sort(a+1,a+n+1);

l=0,r=a[n]-a[1];

while(l<=r)

else

r=mid-1;

}cout<100分**

#include

#define for(i,l,r) for(int i=l;i<=r;++i)

using namespace std;

long long n,m,a[101010],r,l,mid,ans,ct=1,now=1;

bool check(int x)

}return ct>=m?1:0;

}int main()

sort(a+1,a+n+1);

l=0,r=a[n]-a[1];

while(l<=r)

else

r=mid-1;

}cout《真的就是乙個括號讓我調了半個小時啊啊,差點就放棄了

二分查詢 從酒桌遊戲看二分查詢演算法

給前端食堂加星標,吃好每一頓 觀感度 口味 孜然牛肉 本文已收錄在githubgithub.com geekhyt,感謝star。酒桌上曾經玩過這樣乙個小遊戲,遊戲規則是 主持人每次隨機從 1 1000 中選擇乙個數字,比如是 171。只有主持人自己知道並事先寫在紙條上留存,然後分別讓大家來猜,能夠...

(c語言)最大子列和問題(非二分查詢和二分查詢)

給定k個整數組成的序列,連續子列 被定義為,其中 1 i j k。最大子列和 則被定義為所有連續子列元素的和中最大者。例如給定序列,其連續子列有最大的和20。現要求你編寫程式,計算給定整數序列的最大子列和。本題旨在測試各種不同的演算法在各種資料情況下的表現。各組測試資料特點如下 資料1 與樣例等價,...

二分查詢的越界和死迴圈問題

個人看來,二分查詢有兩個易錯點,那就是 越界 和 死迴圈 這裡拿左閉右閉和左閉右開來說,1 左閉右開 int binarysearch seqlist s,datatype x else if s array mid x else 這裡int left 0,right s size while le...