HW13-二分
A:月度開銷
描述
農(nóng)夫約翰是一個(gè)精明的會(huì)計(jì)師。他意識(shí)到自己可能沒有足夠的錢來(lái)維持農(nóng)場(chǎng)的運(yùn)轉(zhuǎn)了。他計(jì)算出并記錄下了接下來(lái)N(1 ≤N≤ 100,000) 天里每天需要的開銷。
約翰打算為連續(xù)的M(1 ≤M≤N) 個(gè)財(cái)政周期創(chuàng)建預(yù)算案,他把一個(gè)財(cái)政周期命名為fajo月。每個(gè)fajo月包含一天或連續(xù)的多天,每天被恰好包含在一個(gè)fajo月里。
約翰的目標(biāo)是合理安排每個(gè)fajo月包含的天數(shù),使得開銷最多的fajo月的開銷盡可能少。
輸入
第一行包含兩個(gè)整數(shù)N,M,用單個(gè)空格隔開。
接下來(lái)N行,每行包含一個(gè)1到10000之間的整數(shù),按順序給出接下來(lái)N天里每天的開銷。
輸出
一個(gè)整數(shù),即最大月度開銷的最小值。
樣例輸入
7 5 100 400 300 100 500 101 400
樣例輸出
500
提示
若約翰將前兩天作為一個(gè)月,第三、四兩天作為一個(gè)月,最后三天每天作為一個(gè)月,則最大月度開銷為500。其他任何分配方案都會(huì)比這個(gè)值更大。
1 #include<iostream>
2 using namespace std;
3 int a[100005];
4 int n, m;
5 bool check(int mid){
6 int cursum = 0;
7 int temp = 1; //注意是1
8 for(int i = 0; i < n; i++){
9 if(cursum+a[i]>mid){ //超了
10 temp++; //fajo月
11 cursum = a[i];
12 }
13 else cursum += a[i];
14 }
15 if(temp > m) return false;
16 return true;
17 }
18 int main(){
19 cin>>n>>m;
20 int l = 0, r = 0;
21 for(int i = 0; i < n; i++){
22 cin>>a[i];
23 l = max(l, a[i]);
24 r += a[i];
25 }
26 int mid;
27 while(l <= r){
28 mid = l + (r-l)/2;
29 bool flag = check(mid);
30 if(flag) r = mid-1;
31 else l = mid+1;
32 }
33 cout<<mid<<endl;
34 return 0;
35 }
備注:二分的題都很套路。首先是卡范圍,最小的可能值顯然就是最多的一天,最大的可能值是所有天之和。然后就是check函數(shù),怎么驗(yàn)證一個(gè)解對(duì)不對(duì),一般就用到貪心。注意括號(hào)里是l<=r,否則假如答案是50,l是50,r是52,mid是51,check過(guò)了之后r變?yōu)?0,不滿足l<r直接退出了,那么答案就不是最小的。這道題就是盡可能把更多的天塞進(jìn)一個(gè)fajo。因?yàn)橐粋€(gè)月如果本可以塞前一個(gè)fajo里卻沒塞,相當(dāng)于一種浪費(fèi),因?yàn)樗鸵紦?jù)后面的空間。這樣塞完之后得到的fajo月如果小于等于我們正在嘗試的mid就是一個(gè)可行的解。
B:派
描述
我的生日要到了!根據(jù)習(xí)俗,我需要將一些派分給大家。我有N個(gè)不同口味、不同大小的派。有F個(gè)朋友會(huì)來(lái)參加我的派對(duì),每個(gè)人會(huì)拿到一塊派(必須一個(gè)派的一塊,不能由幾個(gè)派的小塊拼成;可以是一整個(gè)派)。
我的朋友們都特別小氣,如果有人拿到更大的一塊,就會(huì)開始抱怨。因此所有人拿到的派是同樣大小的(但不需要是同樣形狀的),雖然這樣有些派會(huì)被浪費(fèi),但總比搞砸整個(gè)派對(duì)好。當(dāng)然,我也要給自己留一塊,而這一塊也要和其他人的同樣大小。
請(qǐng)問我們每個(gè)人拿到的派最大是多少?每個(gè)派都是一個(gè)高為1,半徑不等的圓柱體。
輸入
第一行包含兩個(gè)正整數(shù)N和F,1 ≤ N, F ≤ 10 000,表示派的數(shù)量和朋友的數(shù)量。
第二行包含N個(gè)1到10000之間的整數(shù),表示每個(gè)派的半徑。
輸出
輸出每個(gè)人能得到的最大的派的體積,精確到小數(shù)點(diǎn)后三位。
樣例輸入
3 3 4 3 3
樣例輸出
25.133
1 #include<iostream>
2 #include<cmath>
3 using namespace std;
4 double a[10005];
5 int n, f;
6 //注意數(shù)據(jù)類型是double
7 const double pi = acos(-1); //求派的巧妙方法
8 bool check(double mid){
9 int temp = 0;
10 for(int i = 0; i < n; i++)
11 temp += a[i]/mid;
12 return temp >= f+1; //別忘了加上自己
13 }
14 int main(){
15 cin>>n>>f;
16 double l = 0, r = 0;
17 int semi;
18 for(int i = 0; i < n; i++){
19 cin>>semi;
20 a[i] = pi * semi * semi; //存體積
21 r = max(r, a[i]);
22 }
23 double mid;
24 while(r-l > 1e-5){
25 mid = l + (r-l)/2;
26 bool flag = check(mid);
27 if(flag) l = mid;
28 else r = mid;
29 }
30 printf("%.3lf", mid);
31 return 0;
32 }
備注:這道題要注意的就是輸入數(shù)據(jù)是半徑,但問的是體積(高是1,所以相當(dāng)于面積),所以數(shù)組里要存的也是面積。還有一點(diǎn)就是所有的數(shù)據(jù)類型都要是double!最開始我就是忽略了形參mid也應(yīng)該是double所以得不出正確答案。既然是double,就不能用簡(jiǎn)單的=或者<>,而要用ε,這里取的是1e-5。
C:河中跳房子
描述
每年奶牛們都要舉辦各種特殊版本的跳房子比賽,包括在河里從一個(gè)巖石跳到另一個(gè)巖石。這項(xiàng)激動(dòng)人心的活動(dòng)在一條長(zhǎng)長(zhǎng)的筆直河道中進(jìn)行,在起點(diǎn)和離起點(diǎn)L遠(yuǎn)(1 ≤L≤ 1,000,000,000) 的終點(diǎn)處均有一個(gè)巖石。在起點(diǎn)和終點(diǎn)之間,有N(0 ≤N≤ 50,000) 個(gè)巖石,每個(gè)巖石與起點(diǎn)的距離分別為Di(0 <Di<L)。
在比賽過(guò)程中,奶牛輪流從起點(diǎn)出發(fā),嘗試到達(dá)終點(diǎn),每一步只能從一個(gè)巖石跳到另一個(gè)巖石。當(dāng)然,實(shí)力不濟(jì)的奶牛是沒有辦法完成目標(biāo)的。
農(nóng)夫約翰為他的奶牛們感到自豪并且年年都觀看了這項(xiàng)比賽。但隨著時(shí)間的推移,看著其他農(nóng)夫的膽小奶牛們?cè)谙嗑嗪芙膸r石之間緩慢前行,他感到非常厭煩。他計(jì)劃移走一些巖石,使得從起點(diǎn)到終點(diǎn)的過(guò)程中,最短的跳躍距離最長(zhǎng)。他可以移走除起點(diǎn)和終點(diǎn)外的至多M(0 ≤M≤N) 個(gè)巖石。
請(qǐng)幫助約翰確定移走這些巖石后,最長(zhǎng)可能的最短跳躍距離是多少?
輸入
第一行包含三個(gè)整數(shù)L, N, M,相鄰兩個(gè)整數(shù)之間用單個(gè)空格隔開。
接下來(lái)N行,每行一個(gè)整數(shù),表示每個(gè)巖石與起點(diǎn)的距離。巖石按與起點(diǎn)距離從近到遠(yuǎn)給出,且不會(huì)有兩個(gè)巖石出現(xiàn)在同一個(gè)位置。
輸出
一個(gè)整數(shù),最長(zhǎng)可能的最短跳躍距離。
樣例輸入
25 5 2 2 11 14 17 21
樣例輸出
4
提示在移除位于2和14的兩個(gè)巖石之后,最短跳躍距離為4(從17到21或從21到25)。
1 #include<iostream>
2 using namespace std;
3 int a[50005];
4 int L, n, m;
5
6 bool check(int mid){
7 int last = 0;
8 int temp = 0;
9 for(int i = 0; i <= n; i++){
10 if(a[i]-last<mid){
11 temp++; //移走
12 continue;
13 }
14 last = a[i];
15 }
16
17 if(temp > m) return false;
18 return true;
19 }
20 int main(){
21 cin>>L>>n>>m;
22 int r = L, l = 0;
23 for(int i = 0; i < n; i++){
24 cin>>a[i];
25 }
26 a[n] = L;
27 int mid, ans;
28 while(l <= r){
29 mid = l + (r-l)/2;
30 bool flag = check(mid);
31 if(flag){
32 l = mid+1;
33 ans = mid;
34 }
35 else r = mid-1;
36 }
37 cout<<ans<<endl;
38 return 0;
39 }
備注:這道題和我多年前寫過(guò)的跳石頭是一樣的,好像是noip真題。但是首先是我又不會(huì)了,其次是我發(fā)現(xiàn)當(dāng)時(shí)寫的有一點(diǎn)小錯(cuò)(沒考慮最后一塊磚),過(guò)了是因?yàn)閿?shù)據(jù)水……要注意的第一點(diǎn)是要把a(bǔ)[n] = L也就是最后一塊石頭補(bǔ)上。(最后一塊石頭是移不了的,但這不影響結(jié)果,因?yàn)樵赾heck的時(shí)候,如果發(fā)現(xiàn)a[n]-last不合要求,移走的其實(shí)是last那塊石頭。因?yàn)閘ast距離倒數(shù)第三塊石頭一定是滿足要求的,移走它,倒數(shù)第三塊石頭離最后一塊石頭肯定也是足夠的)。
還有一點(diǎn)是上次也強(qiáng)調(diào)過(guò)的。這道題的答案寫mid就不行,舉個(gè)例子,如果答案是51,l是50,r是52,那么mid是51,然后check過(guò)了,l變成52,l依然<=r,這個(gè)時(shí)候依然進(jìn)入循環(huán),mid是52,但mid可能沒通過(guò)check,這個(gè)時(shí)候r變成51,跳出了循環(huán),但這個(gè)時(shí)候mid就不是正確答案。解決辦法就是設(shè)置一個(gè)ans,保證是check過(guò)的。
總結(jié)
- 上一篇: 淘宝客推广链接如何转换?
- 下一篇: ARM Trusted Firmware