二分答案
!閱讀須知||閱讀本博文前筆者認為讀者已經學會(或了解)了:
1.基礎語言與算法
2.標准二分法(二分思想)
3.二分查找
定義
二分答案與二分查找類似,即對有着單調性的答案進行二分,大多數情況下用於求解滿足某種條件下的最大(小)值。
答案單調性
答案的單調性大多數情況下可以轉化為一個函數,其單調性證明多種多樣,如下:
- 移動石頭的個數越多,答案越大(NOIP2015跳石頭)。
- 前i天的條件一定比前 i + 1 天條件更容易(NOIP2012借教室)。
- 滿足更少分配要求比滿足更多的要求更容易(NOIP2010關押罪犯)。
- 滿足更大最大值比滿足更小最大值的要求更容易(NOIP2015運輸計划)。
- 時間越長,越容易滿足條件(NOIP2012疫情控制)。
可以解決的問題
- 求最大的最小值(NOIP2015跳石頭)。
- 求最小的最大值(NOIP2010關押罪犯)。
- 求滿足條件下的最小(大)值。
- 求最靠近一個值的值。
- 求最小的能滿足條件的代價。
代碼
為了保證解在二分搜索的區間里,故不同的問題有着不同(但相似)的寫法,讀者可以畫一個區間模擬一下~
題目解析
1.假定一個解並判斷是否可行(POJ 1064)
鏈接:http://poj.org/problem?id=1064
Cable master
Time Limit: 1000MS Memory Limit: 10000K
Description
To buy network cables, the Judging Committee has contacted a local network solutions provider with a request to sell for them a specified number of cables with equal lengths. The Judging Committee wants the cables to be as long as possible to sit contestants as far from each other as possible.
The Cable Master of the company was assigned to the task. He knows the length of each cable in the stock up to a centimeter,and he can cut them with a centimeter precision being told the length of the pieces he must cut. However, this time, the length is not known and the Cable Master is completely puzzled.
You are to help the Cable Master, by writing a program that will determine the maximal possible length of a cable piece that can be cut from the cables in the stock, to get the specified number of pieces.
Input
Output
If it is not possible to cut the requested number of pieces each one being at least one centimeter long, then the output file must contain the single number "0.00" (without quotes).
Sample Input
4 11 8.02 7.43 4.57 5.39
Sample Output
2.00
題目大意:有n條繩子,長度分別為L[i]。如果從他們中切割出k條長度相同的繩子的話,這k條繩子每條最長能有多長?(答案保留小數點后兩位,規定1單位長度的繩子最多可以切割成100份)。
分析:二分搜索最大長度x。我們令C(x)為可以得到K條長度為x的繩子,那么問題就變為了求滿足條件C(x)的最大的x。在區間初始化時,只需使用充分大的數inf(大於繩子的最大長度的二倍)作為上界即可:left=0,right=inf。那么現在的問題就變為了如何高效的判斷C(x)是否滿足。由於長度為L的繩子最多可以切割出floor(L/x)段長度為x的繩子,因子C(x)=floor(Li/x)的總和是否不小於k,他可以在O(n)的時間內判斷出來。
AC代碼:
1 #include <cstdio> 2 #include <cmath> 3 using namespace std; 4 const int M=10005; 5 const double inf=200005.0; 6 double L[M]; 7 int n,k; 8 bool judge(double x) 9 { 10 int num=0; 11 for(int i=0;i<n;i++) 12 num+=(int)(L[i]/x); 13 return num>=k; 14 } 15 void solve() 16 { 17 double left=0,right=inf; 18 for(int i=0;i<100;i++) //代替while(r>l) 避免了精度問題 19 { //1次循環可以把區間縮小一半,100次可以達到10^(-30)的精度 20 double mid=(left+right)/2; 21 if(judge(mid)) left=mid; 22 else right=mid; 23 } 24 printf("%.2f\n",floor(right*100)/100); 25 } 26 int main() 27 { 28 while(scanf("%d%d",&n,&k)!=-1) 29 { 30 for(int i=0;i<n;i++) 31 scanf("%lf",&L[i]); 32 solve(); 33 } 34 return 0; 35 }
2.最大化最小值(POJ 2456)
鏈接:http://poj.org/problem?id=2456
Description
His C (2 <= C <= N) cows don't like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?
Input
* Lines 2..N+1: Line i+1 contains an integer stall location, xi
Output
Sample Input
5 3 1 2 8 4 9
Sample Output
3
Hint
OUTPUT DETAILS:
FJ can put his 3 cows in the stalls at positions 1, 4 and 8, resulting in a minimum distance of 3.
Huge input data,scanf is recommended.
題意概述:農夫有c頭牛,n個隔間,c頭牛很躁動,很容易相互打架,因此農夫想把它們分得越遠越好,要你分配隔間使得相鄰兩頭牛的距離越遠越好,問你這c頭牛分割的最小距離的最大值。
解題思路:先對隔間的坐標排序,對於牛,最小距離是0,最大距離不會超過兩端兩頭牛的距離值,因此二分地查找分割距離的最大值,每次mid都judge一次,judge的時候貪心地放置牛,保證前i頭牛是符合這樣分割標准的。(二分如果拿捏不好精度,最好就直接暴力100次。)
AC代碼:
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #define INF 0x7fffffff 5 #define maxn 100100 6 using namespace std; 7 int a[maxn]; 8 int n, c; 9 10 int judge(int m) 11 { 12 int last = 0; 13 for (int i = 1; i < c; i++) 14 { 15 int cur = last + 1; 16 while (cur < n && a[cur] - a[last] < m) 17 cur++; 18 if (cur == n) 19 return 0; 20 last = cur; 21 } 22 return 1; 23 } 24 int main() 25 { 26 while (scanf("%d%d", &n, &c) != EOF) 27 { 28 for (int i = 0; i < n; i++) 29 scanf("%d", &a[i]); 30 sort(a, a + n); 31 int l = 0, r = INF, m; 32 for (int i = 0; i < 100; i++) 33 { 34 m = l + (r - l) / 2; 35 if (judge(m)) 36 l = m; 37 else 38 r = m; 39 } 40 printf("%d\n", l); 41 } 42 return 0; 43 }
3.最大化平均值(POJ 2976)
鏈接:http://poj.org/problem?id=2976
Description
In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be
.
Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.
Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without dropping any tests, your cumulative average is . However, if you drop the third test, your cumulative average becomes
.
Input
The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ ai ≤ bi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.
Output
For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.
Sample Input
3 1 5 0 2 5 1 6 4 2 1 2 7 9 5 6 7 9 0 0
Sample Output
83 100
Hint
To avoid ambiguities due to rounding errors, the judge tests have been constructed so that all answers are at least 0.001 away from a decision boundary (i.e., you can assume that the average is never 83.4997).
題目大意:給定n個二元組(a,b),扔掉k個二元組,使得剩下的a元素之和與b元素之和的比率最大。
題目求的是 max(∑a[i] * x[i] / (b[i] * x[i])) 其中a,b都是一一對應的。 x[i]取0,1 並且 ∑x[i] = n - k;
那么可以轉化一下。 令r = ∑a[i] * x[i] / (b[i] * x[i]) 則必然∑a[i] * x[i] - ∑b[i] * x[i] * r= 0;(條件1)
並且任意的 ∑a[i] * x[i] - ∑b[i] * x[i] * max(r) <= 0 (條件2,只有當∑a[i] * x[i] / (b[i] * x[i]) = max(r) 條件2中等號才成立)
然后就可以枚舉r , 對枚舉的r, 求Q(r) = ∑a[i] * x[i] - ∑b[i] * x[i] * r 的最大值, 為什么要求最大值呢? 因為我們之前知道了條件2,所以當我們枚舉到r為max(r)的值時,顯然對於所有的情況Q(r)都會小於等於0,並且Q(r)的最大值一定是0.而我們求最大值的目的就是尋找Q(r)=0的可能性,這樣就滿足了條件1,最后就是枚舉使得Q(r)恰好等於0時就找到了max(r)。而如果能Q(r)>0 說明該r值是偏小的,並且可能存在Q(r)=0,而Q(r)<0的話,很明顯是r值偏大的,因為max(r)都是使Q(r)最大值為0,說明不可能存在Q(r)=0了。
AC代碼:
1 #include <iostream> 2 #include <string> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 #include <algorithm> 7 using namespace std; 8 const int maxn=1002; 9 const double eps=1e-7; 10 int n,k; 11 double a[maxn]; 12 double b[maxn]; 13 int main() 14 { 15 while(cin>>n>>k) 16 { 17 if(n==0&&k==0) 18 break; 19 for(int i=0; i<n; i++) 20 scanf("%lf",&a[i]); 21 for(int j=0; j<n; j++) 22 scanf("%lf",&b[j]); 23 24 double L=0.0; 25 double R=1.0; 26 double mid; 27 28 double t[1004]; 29 30 while(R-L>eps) 31 { 32 mid=(R+L)*1.0/2; 33 34 for(int i = 0; i < n; i++) 35 t[i] = a[i] - mid * b[i]; 36 sort(t, t + n); 37 double sum = 0; 38 for(int i = k; i < n; i++) 39 sum += t[i]; 40 41 if(sum>0) 42 L=mid; 43 else 44 R=mid; 45 } 46 printf("%.0f\n",mid*100); 47 } 48 return 0; 49 }