二分法入门——POJ1064 ,POJ2456 ,POJ3111

来源:互联网 发布:python 发送邮件附件 编辑:程序博客网 时间:2024/06/16 06:50

二分法,顾名思义就是将求解区间从中间分为两部分,寻找最优解所在区间,通过不断地将求解区间减半,而达到快速逼近最优解的目的。
通常做法就是在左界限L和右界限R这段区间的中间取mid=(L+R)/2,通过对mid处满足问题要求与否来决定留下左区间还是右区间,通过L=mid或R=mid来缩小区间。
二分法的结束条件可以用左右界限差值如while(R-L>0.01)或者指定次数的循环for(int i=0;i<100;i++)来实现,注意浮点数区间可能会因为浮点数的误差而导致使用左右界限来判定结束二分陷入死循环,所以不同问题要具体分析做出不同的选择。
POJ1064 ,POJ2456 ,POJ3111等题目都是通过二分寻找满足条件的最优值。


Cable master POJ - 1064

将n段绳子切割出k段相同长度,不能拼接,求解可行的长度最大值。
通过二分法逼近答案,每次用O(n)的时间判断满足可行与否。

#include<iostream>#include<string>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#include<cstring>using namespace std;int n, k;double len[10005];double star,endd;int main(){    while (~scanf("%d%d", &n, &k)){        star = endd = 0;        for (int i = 0; i < n; i++){ scanf("%lf", &len[i]); endd += len[i]; }        endd *= 2;        double mid;        int num;        for (int i = 0; i <= 100;i++){            mid = (star + endd) / 2;            num = 0;            for (int i = 0; i < n; i++){                num += int(len[i] / mid);            }            if (num >= k){ star = mid; }            else{ endd = mid; }        }        printf("%.2lf\n", floor(endd*100)/100);    }    return 0;}

Aggressive cows POJ - 2456

给出n个牛舍的坐标,要求安排k头牛,求相邻牛之间最小距离的最大值。(最大化最小值)思路与上面相似,区间变为整型区间,同样可以在O(n)内判断出满足条件与否。

#include<iostream>#include<string>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#include<cstring>using namespace std;int n, k;int p[100005];int star,endd;int main(){    while (~scanf("%d%d", &n, &k)){        star = 0;        endd = 0x3f3f3f;        for (int i = 0; i < n; i++){ scanf("%d", &p[i]); }        sort(p, p + n);        int mid;        int num,cnt;        while(endd-star>1){            mid = (star + endd) / 2;            num = 1;            cnt = p[0];            for (int i = 1; i < n; i++){                if ((p[i] - cnt) >= mid){                    num++;                    cnt = p[i];                }                if (num == k)break;            }            if (num >= k){ star = mid; }            else{ endd = mid; }        }        printf("%d\n", star);    }    return 0;}

K Best POJ - 3111

从n个物品里选出k个物品,使得价值总和/重量总和最大。
易证贪心是错误的,思路是二分法答案,二分判断时要求选出k个物品
使得(v1+v2+…+vk)/(w1+w2+…+wk)>=mid.
则(v1+v2+…+vk)- (w1+w2+…+wk)*mid>=0;因此只要贪心选出v-w*mid最大得k个物品即可。

#include<iostream>#include<stdio.h>#include<algorithm>#include<math.h>using namespace std;int n, k;int v[100005], w[100005];struct pp{    int num;    double v;}p[100005];bool cmp(pp a, pp b){    return a.v > b.v;}int main(){    while (~scanf("%d%d", &n, &k)){        for (int i = 0; i < n; i++){            scanf("%d%d", &v[i], &w[i]);        }        double beg=0, endd=0x3f3f3f, mid;        for (int i = 0; i < 50; i++){            mid = (beg + endd) / 2;            for (int i = 0; i < n; i++){                p[i].num = i + 1; p[i].v = double(v[i]) - w[i] * mid;            }            sort(p, p + n, cmp);            double total = 0;            for (int i = 0; i < k; i++){ total += p[i].v; }            if (total >= 0){ beg = mid; }            else{ endd = mid; }        }        for (int i = 0; i < k; i++){            printf("%d", p[i].num);            if (i < k - 1){ printf(" "); }        }        printf("\n");    }    return 0;}

如有任何错误或者不足,欢迎交流指正。


0 0
原创粉丝点击