基础算法——二分

  上次我们讲了贪心,上题:

题目描述:
RFdragon摘了n种果子,每种果子堆成一堆,现在RFdragon打算将所有果子运回家,因此决定将所有果子合并成一堆。每次RFdragon可以选择任意两堆果子进行合并,消耗等同于两堆果子质量之和的力气。请你求出一个合并方案,使得RFdragon消耗的力气最少。

输入描述:
第一行一个正整数n。
第二行n个正整数,表示每堆果子的质量。

输出描述:
一个正整数,表示消耗力气的最小值。

输入样例:
5
3 2 5 2 1

输出样例:
29

其他说明:
n<100000,所有数据均在int范围内。

  今天我们要讲的是二分。设想这样一个场景:让你从1~100之间猜一个数,每次告诉你大了或者小了,要求尝试次数最少,你会猜几?想都不用想,肯定先猜50。这个时候,你已经在用二分的思想来解决问题了。二分主要分两种:二分查找和二分答案。我们先来看二分查找。我们直接来看一道题:

题目描述:
输入n个数,将n个数从小到大排序,问k排在第几。

输入描述:
第一行两个正整数n和k,中间用一个空格隔开。
第二行有n个正整数,中间有一个空格隔开。

输出描述:
一个正整数,表示k排在第几位。

输入样例:
5 7
7 3 5 6 9

输出样例:
4

其他说明:
n<1000000,所有输入数据均在int范围内,保证没有重复的数。

  它要怎么用二分来实现呢?首先,不用说,肯定要将输入的所有书进行排序。那之后呢?我们还回到刚才猜数的例子。假设你猜了50,告诉你50小了,你就排除了1~50之间的所有数,范围变为51~100;你再猜75,假如75也小了,那你又排除了51~75之间的所有数,范围变为76~100。因此,我们需要两个数来记录数据的范围。我一般喜欢用head和tail进行表示,如下:

int head=1,tail=n;

  head表示最小值,tail表示最大值。为什么tail要等于n呢?这是因为,n是数组中最大的数的下标,这个数再大,下标也不可能超过n。每次取一个中间值,表示我们“猜”的那个数。

mid=(head+tail)/2;
if(a[mid]>k)
    tail=mid-1;
else if(a[mid]<k)
    head=mid+1;
else
{
    printf("%d",mid);
    return 0;
}

  这就是我们“猜”数并缩小范围的过程。掌握了核心代码,题就不难做了。完整代码如下:

#include<cstdio>
int n,k,a[1000000],head=1,tail,mid;
int main()
{
    scanf("%d %d",&n,&k);
    tail=n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    while(head<=tail)
    {
        mid=(head+tail)/2;
        if(a[mid]==k)
        {
            printf("%d",mid);
            return 0;
        }
        else if(a[mid]>k)
            tail=mid-1;
        else
            head=mid+1;
    }
    return 0;
} 

  这就是二分查找。那么二分答案又是什么呢?有一些题中,答案很难被直接确定,需要先确定范围,然后在范围中逐渐尝试。用一般的查找方法自然很慢,但用二分来查找答案就快的多了。为了让大家更好理解二分答案,先上一道题:

题目描述:
RFdragon家里有一些废品,由于废品实在太多了,因此需要分批运走。黑心的收废品的叔叔为了盈利,不但不给钱,反而还向RFdragon索要工钱。RFdragon好心的同意了。
RFdragon会将所有的废品分成n批运走,运走每批废品都要花一定量的钱。由于叔叔很黑心,因此向RFdragon提出以下要求:RFdragon每天给叔叔k元钱,k是一个定值,叔叔会运走几批废品,但是这几批废品必须是连续的,并且每次必须将一整批全部运走;如果运走几批废品后钱有剩余,但不够运走下一批废品,那么剩下的钱会被叔叔收入腰包。由于忍受不了废品散发的味道,RFdragon要求运走所有废品的天数不能超过m天,并且k越小越好。请你帮他求出k的最小值。

输入描述:
第一行三个正整数,中间用空格隔开,分别表示n、m。
第二行有n个正整数,表示运走每批废品的价格。

输出描述:
一个正整数,表示k的最小值。

其他说明:
所有数据<10000。

  首先,我们要确定二分范围。最小值一定是所有废品中花钱最多的那个,最大值一定是所有废品所花钱数之和。问题来了,我们每次确定一个mid值之后,如何判断这个mid值是否可以满足题意呢?我们不妨写一个check函数。

bool check(int x)//参数x表示假设k=x
{
    int cur=x,sum=1;//cur表示当前剩余钱数,sum表示运走所有废品所需天数
    for(int i=1;i<=n;i++)
    {
        if(cur>a[i])
            cur-=a[i];//假设剩余钱数能运走当前这批废品,就让叔叔运走他
        else
        {
            cur=x-a[i];
            sum++;//否则就让叔叔(使用)明天(的钱)运走他
        }
    }
    return sum<=m;
} 

  完整代码如下:

#include<algorithm>
#include<cstdio>
using namespace std;
int n,m,a[10000];
bool check(int x)
{
    int cur=x,sum=1;
    for(int i=1;i<=n;i++)
    {
        if(cur>a[i])
            cur-=a[i];
        else
        {
            cur=x-a[i];
            sum++;
        }
    }
    return sum<=m;
}
int main()
{
    int head=0,tail=0,mid;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        tail+=a[i];
        head=max(head,a[i]);
    }
    while(head<tail)
    {
        mid=(head+tail)/2;
        if(check(mid))
            tail=mid;
        else
            head=mid+1;
    }
    printf("%d",head);
    return 0;
}

  这就是二分的用法,你学会了吗?

//答案代码
#include<algorithm>
#include<cstdio>
using namespace std;
int n,a[10000],ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        a[i]+=a[i-1];
        ans+=a[i];
    }
    printf("%d",ans);
    return 0;
}

Created by RFdragon

原文地址:https://www.cnblogs.com/RFdragon/p/10828772.html

时间: 2024-08-30 17:05:04

基础算法——二分的相关文章

【信息学奥赛一本通】Part1.2 基础算法-二分与三分

问题 A: [二分和三分]愤怒的牛 题目描述 农夫约翰建造了一座有n间牛舍的小屋,牛舍排在一条直线上,第i间牛舍在xi的位置,但是约翰的m头牛对小屋很不满意,因此经常互相攻击.约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍.也就是要最大化最近的两头牛之间的距离. 牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗.为了不让牛互相伤害.John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢? 输入 第一行用空格分

基础算法 - 二分查找

public class Main { private static int binary_search(int[] x, int key) { int l = 0, r = x.length - 1; while (l <= r) { int m = (l + r) >>> 1; if (x[m] == key) { return m; } else if (x[m] < key) { ++ l; } else { -- r; } } return -1; } privat

算法——基础篇——二分查找

     二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.     首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功

Python 迭代器&amp;生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致 迭代器&

PHP基础算法

1.首先来画个菱形玩玩,很多人学C时在书上都画过,咱们用PHP画下,画了一半. 思路:多少行for一次,然后在里面空格和星号for一次. <?php for($i=0;$i<=3;$i++){ for($j=0;$j<=3-$i;$j++){ echo ' '; } for($k=0;$k<=2*$i;$k++){ echo '*'; } echo '<br/>'; } 2.冒泡排序,C里基础算法,从小到大对一组数排序. 思路:这题从小到大,第一轮排最小,第二轮排第二小

有动态示意图!程序员必须知道的10大基础算法讲解

目录: 算法一:快速排序算法 算法二:堆排序算法 算法三:归并排序 算法四:二分查找算法 算法五:BFPRT(线性查找算法) 算法六:DFS(深度优先搜索) 算法七:BFS(广度优先搜索) 算法八:Dijkstra算法 算法九:动态规划算法 算法十:朴素贝叶斯分类算法 算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n

基础算法之排序--快速排序

1 /************************************************************************************** 2 * Function : 快速排序 3 * Create Date : 2014/04/21 4 * Author : NTSK13 5 * Email : [email protected] 6 * Copyright : 欢迎大家和我一起交流学习,转载请保持源文件的完整性. 7 * 任何单位和个人不经本人允许不

九章算法 基础算法 强化算法 系统设计 大数据 安卓 leetcode 高清视频

leetcode 直播视频讲座录像 九章算法视频录像,PPT 算法班,算法强化班,Java入门与基础算法班,big data项目实战班,Andriod项目实战班 九章算法下载 九章算法面试 九章算法leetcode 九章算法答案 九章算法mitbbs 九章算法班 九章算法ppt 九章算法录像 九章算法培训 九章算法微博 leetcode 视频 九章算法偷录 算法培训 算法班课程大纲: 1 从strStr谈面试技巧与Coding Style(免费试听) 2 二分搜索与旋转排序数组 Binary S

算法录 之 基础算法

这一篇文章主要说说一些基础算法,这些算是很多其他算法的基石. 主要包括  模拟,暴力,枚举,递归,贪心,分治. 1,模拟: 顾名思义,模拟就是...饿,模拟,有一些题目就是给你一些步骤,然后你写代码一步步去模拟那些步骤就行.这类题目主要属于基础题,但是当然如果需要模拟的步骤足够恶心的话,还是比较麻烦的... 具体模拟的例子在之后的练习中会遇到的,按照题目要求一步步做就行,一般算法难度比较小. 2,暴力: 顾名思义,暴力就是...饿,暴力.比如说题目让从平面上的10个点中取三个点,让他们形成的三角