很久沒寫blog了嗷。html
挑戰程序設計競賽上二分的標題就是——不光是查找值。因此在這裏總結一下上一週的二分三分訓練。ios
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 }
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 }
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 }
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 }
二分要解決的問題是不嚴格單調序列,可是遇到如單峯函數或單谷函數這樣的狀況,就要用三分了。
以找單峯函數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