好久没写blog了嗷。
挑战程序设计竞赛上二分的标题就是——不光是查找值。所以在这里总结一下上一周的二分三分训练。
零、有序数组中查找某个值(不说了)
一、最大化最小值
POJ2456、POJ3258 这两个题非常像嗷,稍微改下代码就可以了嗷。
我先做的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:
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 }
二、假定一个解判断是否可行/是否满足条件
POJ1759、POJ3104、POJ1064(坑)
其实1759看网上别人的blog还有依次算、依次满足的方法,在这个题里也很快。
1759二分:
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另解:
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 1、对于一件ai值小于等于mid的衣服,直接晾干即可;
3 2、对于一件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 }
四、导数求值
HDU2899、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 }
不过这只是最简单的形式,一般题目里还是要写一个判断条件的函数,再推一个凸函数或凹函数公式,然后依据这个条件对这个函数来做三分。
POJ3296、POJ3737
详细看人家写的嗷:https://blog.csdn.net/pi9nc/article/details/9666627
来源:https://www.cnblogs.com/noobimp/p/10510066.html