二分、三分專題——經典題型略解

很久沒寫blog了嗷。html

挑戰程序設計競賽上二分的標題就是——不光是查找值。因此在這裏總結一下上一週的二分三分訓練。ios

零、有序數組中查找某個值(不說了)

 

1、最大化最小值

POJ245六、POJ3258 這兩個題很是像嗷,稍微改下代碼就能夠了嗷。c++

我先作的3258.數組

2456:函數

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;  5 #define maxn 50050
 6 int l,n,m;  7 int dis[maxn];  8 
 9 bool C(int x){ 10     int last=0; 11     for(int i=1;i<m;i++){//m-1次 12         int cur=last+1;
13         while(cur<n&&dis[cur]-dis[last]<x) 14             cur++; 15         if(cur==n) return 0; 16 
17         last=cur; 18  } 19     return 1; 20 } 21 int main(){ 22     scanf("%d%d",&n,&m); 23     for(int i=0;i<n;i++) scanf("%d",dis+i); 24     sort(dis,dis+n); 25 
26     int le=0,ri=dis[n-1]; 27     while(ri-le>1){ 28         int mid=(ri+le)/2; 29 
30         if(C(mid)) le=mid;//若是放得開,說明x還能夠更大
31         else ri=mid; 32  } 33     printf("%d\n",le); 34     return 0; 35 }

 

3258:spa

 1 /*
 2 一條長L的河上,除了0 和 l 處還有N 個石子,分別距離起點距離di,  3 求去掉M個石子後相鄰的最小距離的最大值。(最大化最小值)  4 和poj2456基本同樣 2456是給每一個點的位置,這個題還要加上一個從0開始,到l處結束,至關於多兩個元素  5 
 6 定義C(x):能夠去掉m個石頭使任意石頭間距不小於x。  7 */
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <algorithm>
12 using namespace std; 13 #define maxn 50050
14 int l,n,m; 15 int dis[maxn]; 16 
17 bool C(int x){ 18     int num=n-m; 19     int last=0;             //刪除num個石頭,循環num次 但2456裏選擇m個點只需循環m-1次,一開始想不明白wa了好久
20     for(int i=0;i<num;i++){//對於這些石頭,要使任意間距不小於x,
21         int cur=last+1; 22         while(cur<=n&&dis[cur]-dis[last]<x)//就要把下一個放入第一個不知足while條件的位置
23             cur++;                        //由cur記錄
24         if(cur>n) return 0;//若是在這個過程當中大於n了,說明放不開
25 
26         last=cur;//更新檢查完的位置
27  } 28     return 1; 29 } 30 int main(){ 31     scanf("%d%d%d",&l,&n,&m); 32     if(n==m) { 33         printf("%d\n",l); 34         return 0; 35  } 36     for(int i=1;i<=n;i++) scanf("%d",dis+i); 37     dis[n+1]=l; 38     sort(dis,dis+n+2); 39 
40     int le=0,ri=l; 41     while(ri-le>1){ 42         int mid=(ri+le)/2; 43 
44         if(C(mid)) le=mid;//若是放得開,說明x還能夠更大
45         else ri=mid; 46  } 47     printf("%d\n",le); 48     return 0; 49 }

 

2、假定一個解判斷是否可行/是否知足條件

POJ175九、POJ310四、POJ1064(坑).net

其實1759看網上別人的blog還有依次算、依次知足的方法,在這個題裏也很快。設計

1759二分:code

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;  4 #define inf 0x3f3f3f3f
 5 #define maxn 1006
 6 #define eps 1e-8//
 7 int n;  8 double A,num[maxn];  9 bool solve(double mid){ 10     num[1]=mid; 11     for(int i=2;i<n;i++){ 12         num[i]=2*num[i-1]+2-num[i-2]; 13         if(num[i]<eps) return false; //寫小於0因爲精度問題會wa
14  } 15     return true; 16 } 17 int main(){ 18     while(scanf("%d%lf",&n,&A)==2){ 19         num[0]=A; 20         double low=-inf; 21         double high=inf; 22         for(int i=0;i<100;i++){ 23             double mid=(low+high)/2; 24             if(solve(mid)) high=mid; 25             else low=mid; 26  } 27         printf("%.2lf\n",num[n-1]); 28  } 29     return 0; 30 }

 

1759另解:htm

 1 /*
 2 H1 = a, H3 = H2 * 2 + 2 - H1, H4 = H3 * 2 + 2 - H2, .....  3 Hn = Hn-1 * 2 + 2 - Hn-2  4 每一個式子都能化簡爲k * H2 + b的形式  5 */
 6 #include <bits/stdc++.h>
 7 using namespace std;  8 #define maxn 1005
 9 
10 int n,k[maxn]; 11 double a,b[maxn],h2; 12 double solve(){ 13     double h2=0; 14     k[1]=0, b[1]=a, k[2]=1, b[2]=0.0;//H1=0 * H2 + a, H2=1 * H2 + 0
15     for (int i = 3; i <= n; i ++) { 16         k[i] = 2 * k [i - 1] - k[i - 2]; 17         b[i] = 2 * b[i - 1] - b[i - 2] + 2; 18         if (h2 * k[i] + b[i] < 0) 19             h2 = -b[i] / k[i];//令此式==0解出h2, 可以使h2儘可能小, 最後乘出來的hn也就最小
20  } 21     return h2*k[n]+b[n]; 22 } 23 int main() { 24     while (~scanf("%d%lf", &n, &a)) { 25         printf("%.2lf\n", solve()); 26  } 27     return 0; 28 }

 

3104:

 1 /*
 2 一、對於一件ai值小於等於mid的衣服,直接晾乾便可;  3 二、對於一件ai值大於mid值的衣服,最少的用時是用機器一段時間,晾乾一段時間  4 C(mid):全部衣物都能幹的最短期mid  5 設這兩段時間分別是x1和x2,那麼有mid=x1+x2,ai<=k*x1+x2,  6 解得x1>=(ai-mid)/(k-1) ,因此對(ai-mid)/(k-1)向上取整就是該件衣服的最少用時。  7 */
 8 //#include<bits/stdc++.h>
 9 #include <iostream>
10 #include <cstdio>
11 #include <cmath>
12 using namespace std; 13 #define ll long long
14 long long a[100005];//用int會WA
15 int n; 16 ll k; 17 bool C(ll x){ 18     ll sum = 0; 19     for (int i = 0; i < n; i++) 20         if (a[i] > x) 21             sum += ceil ( (a[i] - x) * 1.0 / (k - 1));//全部衣服晾乾須要的總時間
22 
23     return sum>x; 24 } 25 
26 int main() { 27     scanf ("%d", &n); 28     ll  maxx = -1; 29     for (int i = 0; i < n; i++) { 30         scanf ("%lld", a+i); 31         maxx=max(maxx,a[i]);// 找出含水量的最大值做爲上界
32  } 33     scanf ("%lld", &k); 34     if (k == 1)//k-1做除數會re
35         printf ("%lld\n", maxx); 36     else { 37         ll left = 1, right = maxx, mid; 38         while (right > left) { 39             mid = (left + right) / 2; 40 
41             if (C(mid)) left = mid+1; 42             else right= mid; 43  } 44         printf ("%lld\n", left); 45  } 46     return 0; 47 }

 

3、最大化平均值

POJ2976

 2976:

 1 ///C(x):a[i]-x*b[i]從大到小排列前n-k個的和大於等於零
 2 //#include <bits/stdc++.h>
 3 #include <iostream>
 4 #include <cstdio>
 5 #include <algorithm>
 6 using namespace std;  7 #define maxn 1005
 8 int n,k,a[maxn],b[maxn];  9 double y[maxn];//a[i]-x*b[i]
10 bool C(double x){ 11     for(int i=0;i<n;i++) y[i]=a[i]-x*b[i]; 12     sort(y,y+n); 13 
14     double sum=0; 15     for(int i=0;i<n-k;i++) sum+=y[n-i-1]; 16 
17     return sum>=0; 18 } 19 int main(){ 20     while(scanf("%d%d",&n,&k)&&(n||k)){ 21         for(int i=0;i<n;i++) scanf("%d",a+i); 22         for(int i=0;i<n;i++) scanf("%d",b+i); 23 
24         double l=0,r=1000; 25         for(int i=0;i<100;i++){ 26             double mid=(l+r)/2; 27             if(C(mid)) l=mid; 28             else r=mid; 29  } 30         printf("%.0lf\n",100.0*l);//四捨五入
31  } 32     return 0; 33 }

 

 

4、導數求值

HDU289九、HDU1724

2899:

 1 #include <iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 const double eps = 1e-8;  5 using namespace std;  6 double hs(double x,double y){//原函數
 7     return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;  8 }  9 double ds(double x,double y){//導函數
10     return 42*pow(x,6)+48*pow(x,5)+21*pow(x,2)+10*pow(x,1)-y; 11 } 12 int main(){ 13     int a; 14     scanf("%d",&a); 15     while(a--){ 16         double b,l,r,mid; 17         scanf("%lf",&b); 18         l=0.0; r=100.0; 19         while(r-l>eps){ 20             mid=(l+r)/2; 21             if(ds(mid,b)>0) r=mid; 22             else l=mid; 23  } 24         printf("%.4lf\n",hs(l,b)); 25  } 26     return 0; 27 }

 

 

1724:用到了辛普森公式,建議補充數學知識。

 1 #include <bits/stdc++.h>
 2 using namespace std;  3 
 4 #define ll long long
 5 const double eps = 1e-10;//1e-8 WA了 
 6 double a, b;  7 double f (double x) {  8     return b * sqrt (1.0 - (x * x) / (a * a));  9 } 10 double simpson (double l, double r) { 11     return (f (l) + 4.0 * f ( (l + r) / 2.0) + f (r)) / 6.0 * (r - l); 12 } 13 double integral (double l, double r) { 14     double mid = (l + r) / 2.0; 15     double res = simpson (l, r); 16     if (fabs (res - simpson (l, mid) - simpson (mid, r)) < eps) return res; 17     else return integral (l, mid) + integral (mid, r); 18 } 19 
20 int main() { 21     int T; scanf("%d",&T); 22     double l, r; 23     while (T--) { 24         scanf("%lf%lf%lf%lf",&a,&b,&l,&r); 25         printf("%.3lf\n",2 * integral (l, r)); 26  } 27     return 0; 28 }

 

5、三分(挖坑)

 二分要解決的問題是不嚴格單調序列,可是遇到如單峯函數或單谷函數這樣的狀況,就要用三分了。

以找單峯函數f(x)最大值爲例:

 1 double fun(double x){  2     //f(x)
 3 }  4 
 5 double tri_search(double left, double right){  6     double midl, midr;  7     while (right-left > eps){  8         midl = (left + right) / 2.0;  9         midr = (midl + right) / 2.0; 10         // 若是是求最小值的話這裏判<=便可
11         if(fun(midl) >= fun(midr)) right = midr; 12         else left = midl; 13  } 14     return left; 15 }

不過這只是最簡單的形式,通常題目裏仍是要寫一個判斷條件的函數,再推一個凸函數或凹函數公式,而後依據這個條件對這個函數來作三分。

POJ329六、POJ3737

詳細看人家寫的嗷:https://blog.csdn.net/pi9nc/article/details/9666627

原文出處:https://www.cnblogs.com/noobimp/p/10510066.html

相關文章
相關標籤/搜索