二分搜索 小讲 【 理解 + 例题 】 更新ing......

    人生路漫漫、、、、

    今天我们来讲一下二分搜索,在刚开始学C语言的时候,就有这样的一段代码,是关于二分查找的,直到大一暑假集训的时候,才知道,二分法可以解决那么多的问题,现在,算是回顾,也算是更深的一步理解吧、 分三种类型来讲一讲、、、

    1、 判断一个解是否可行

    题目 :POJ 1064  Cable master   链接:http://poj.org/problem?id=1064

    该题就是不断地去寻找最长的,符合题意的绳子的长度

   

#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;

double tt[10010];

int main()
{
    int n, m;
    while(~scanf("%d%d",&n,&m))
    {
        double r = 0, l = 1.0;
        if(n == 0 && m == 0)
            break;
        for(int i = 0; i < n; i ++)
        {
            scanf("%lf",&tt[i]);
            r = max(tt[i], r);
        }
        while(r - l > 1e-6)
        {
            double mid = (l + r ) / 2;
            int cnt = 0;
            for(int i = 0; i < n ; i ++)
            {
                cnt += floor(tt[i] / mid);
            }
            if(cnt >= m)
                l = mid;
            else
                r = mid;
        }
        printf("%.2f\n", l);  // 这里要注意,使用%2.lf 是WA 的,不知为啥、
    }
}

    2、 最小值最大化

    最小值最大化,抑或是最大值最小化,都可以用二分搜索来解决,但是要注意的是构建这样的一个模型、、

    POJ 2456  Aggressive cows  链接:http://poj.org/problem?id=2456

    此题中是要最大化最近的两头牛之间的距离。

   

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int tt[200010];
int i, j;
int n, m;

bool judge(int k)
{
    int i,max = tt[0], sum = 0;
    for(i = 1; i < n; i ++)
    {
        if(tt[i] - max >= k)
        {
            sum ++;
            max = tt[i];
        }
        if(sum >= m - 1)
           return true;
    }
    return false;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(i = 0; i < n ; i ++)
            scanf("%d",&tt[i]);
        sort(tt, tt + n);
        int maxn = tt[n - 1] - tt[0];
        int l = 0;
        int tmp = - 1;
        int mid;
        while(l <= maxn)
        {
            mid = (maxn + l) >> 1;
            if(judge(mid))
                l = mid + 1;
            else
                maxn = mid - 1;
        }
        printf("%d\n", l - 1);
    }
    return 0;
}

    3、 最大化平均值

    这个我看了好久,然后也推了好久的公式,后来发现,原来这么地简单 、、、


    题目: 有n 个物品的重量和价值分别是wi 和vi ,从中间选出k 个物品使得单位重量的价值最大。

   1 <= k <= n <= 10^4 , 1 <= wi ,vi <= 10^6

    INPUT :

    n = 3, k = 2;

    {w, v} = {  {2, 2} ,  {5,  3} ,  {2, 1}  };

    OUTPUT :

    0.75 (选1 号 和 3 号, (2 + 1)/ (2 + 2) = 0.75 )


    刚开始拿到这题的时候,就以为是贪心,后来发现,这不是 啊!!

    我们可以这样想,题目是要我们求sum(vi)/ sum (wi)   的最大值, 我们用二分搜索的时候,就是要使sum(vi)/ sum (wi)  尽可能的大,假设存在这样的一个x,使得

sum(vi)/ sum (wi)  >= x ,并且满足题意的情况下,经过变形,就有这样的公式:只要满足 sum(vi - x * wi ) >= 0 即可,所以,剩下来的,只需要对(vi - x * wi ) 的值进行排序贪心地进行选取即可。

   

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define MAXN 10010

int n, k;
int w[MAXN], v[MAXN];
double xx[MAXN];

bool judge(double x)
{
    for(int i = 0; i < n ; i ++)
    {
        xx[i] = v[i] - x * w[i];
    }
    sort(xx, xx + n);
    double sum = 0;
    for(int i = 0; i < k; i ++)  // 贪心的一个过程
    {
        sum += xx[n - i - 1];
    }
    return sum >= 0;
}

int main()
{
	while(~scanf("%d%d",&n,&k))
	{
	    double l = 0, maxn = 0;
		for(int i = 0; i < n; i ++)
        {
            scanf("%d%d",&w[i], &v[i]);
            maxn = max(maxn, v[i] *1.0/ w[i]); //我加的这个,算是优化么-.-
        }
        while(maxn - l >= 1e-6)
        {
            double mid = (l + maxn) / 2;
            if(judge(mid))
                l = mid;
            else
                maxn = mid;
            printf("%.2f%.2f\n", l, maxn);
        }
        printf("%.2f\n",maxn );
	}
}

/*
3 2
2 2
5 3
2 1
*/

  今天突然做到这题,POJ  3111  K Best  链接:http://poj.org/problem?id=3111

    几乎和上面一样的思路,要求一定要取哪个,才能获得最大值,我的思路是这样的,先求出最大值,再用去判断,是否满足,即取出 公式值最大的前k 个数据即可,我这里用的是结构体来存顺序。

   

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define MAXN 100010

int n, k;
struct node
{
    int w, v;  // 宝石的质量和体积
    int num;  // 第num 个 宝石
    double ans; //为了便于排序,所以放在结构体里面了
}ff[MAXN];

double xx[MAXN];

bool cmp(node a, node b)
{
    if(a.ans - b.ans <= 1e-6)
        return false;
    else if(a.ans - b.ans > 1e-6)
        return true;
    return false;
}

bool judge(double x)
{
    for(int i = 0; i < n ; i ++)
    {
        xx[i] = ff[i].v - x * ff[i].w;
    }
    sort(xx, xx + n);
    double sum = 0;
    for(int i = 0; i < k; i ++)  // 贪心的一个过程
    {
        sum += xx[n - i - 1];
    }
    return sum >= 0;
}

int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        double l = 0, maxn = 0;
        for(int i = 0; i < n; i ++)
        {
            scanf("%d%d",&ff[i].v, &ff[i].w);
            maxn = max(maxn, ff[i].v *1.0/ ff[i].w);
            ff[i].num = i + 1;
        }
        while(maxn - l >= 1e-6)
        {
            double mid = (l + maxn) / 2;
            if(judge(mid))
                l = mid;
            else
                maxn = mid;
           // printf("%.2f  %.2f\n", l, maxn);
        }
        //printf("%.2f\n",maxn );
        for(int i = 0; i < n; i ++)
        {
            ff[i].ans = ff[i].v - ff[i].w * maxn;
        }
        sort(ff, ff + n, cmp);
        printf("%d",ff[0].num);
        for(int i = 1; i < k ; i ++) // 取出满足题意的前k 个即可
        {
            printf(" %d", ff[i].num);
        }
        printf("\n");
    }
}


  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值