#2020寒假集训#二分入门(Binary Search)代码笔记

二分查找——Binary Search

原理就类似于下图啦(网上看到哒,忽略这个代价)
二分的时间复杂度一般是 O(logN) 的,超开心了有木有٩(๑>◡<๑)۶
最简单来说,它可以分为整数二分和实数二分两类题型,套路和注意点都值得思考喔
在这里插入图片描述

整数二分

Rikka with Mutex
hznu19training题源

Background
Sometimes, technical terms implicate some life philosophy. Mutex is one of them. On your way to dream, you may be locked by some difficulties, and you need someone to stop his step, and help you get through them.
To help you know better about the life philosophy inside mutex, Rikka comes up with a simple task. Maybe some of you know little about mutex, so she uses another scene to replace it.
There are n gates in a row, several people in the left side of the gates and all of them want to go to the right side. There are two kinds of gates: black and white. These people share energy, which is represented by a non-negative number E. Initially, E=0.
If one person walks through a white gate, he will gain one point of energy, i.e., E will be added by 1. And if one person walks through a black gate, he will lose one point of energy, i.e., E will be subtracted by 1. Since E must be a non-negative integer, if E=0, no one can walk through a black gate until someone walks through a white gate. You can assume there won’t be two people moving at the same time and all the people are selfless.
We use P to represent a black gate, V to represent a white gate and use a PV string to represent the row. Initially, all the people are at the beginning of the string, and all of them want to go through the whole string. But unfortunately, sometimes it may be impossible. So, they want to send at least one person to the right side.
Your task is to find out the minimal number of people which this group needs to achieve this goal.
For example, if the row is VPP, they need at least two people: The first person walk through the first white gate and the second person can use this point of energy to go through the whole string.

Input
The first line contains a single numner t(1≤t≤103), the number
of the testcases. For each testcase, the first line contains a PV
string s(1≤|s|≤105) describing the gates. The input guarantees that
there are at most 30 testcases with |S|>1000.

Output
For each testcase, output a single integer, the answer. And if it is impossible, output −1.

Sample Input
4
VPP
VPPVVVVPPPPPPPP
VPPPPPPPPPPPPPP
P

Sample Output
2
3
14
-1

理解-中文题意
  • 输入一个字符串string,字符串的长度就是门的个数,V代表白门,P代表黑门
  • 一个人通过一扇V时,总的能量增加1,通过一扇黑门,总的能量减少1
  • 当能量小于0的时候不能通过黑门
  • 初始能量每个人都是0,那么总能量池也是0
  • 问至少一个人通过全部门,最小需要几个人
注意-题意敲重点
  • 所有人共用能量,无论谁通过了哪扇门,总的能量池都会变化
  • 比如10个人一起通过V,那么能量池里的能量就+10
  • 最后的目的就是,总的能量够至少一个人自己走到终点
思路-情景模拟
  • 如果有一排门VVVPPPP
  • 可以先让一个人去试探——冲锋陷阵.jpg
  • 如果他往前走到某个位置,总能量上涨
  • 那么其余的人都可以跟上来到这个位置
  • 因为一个人涨,大家一起过来,就会涨得更多
  • 一个人跌,大家一起过来,只会跌得更多
  • 所以要保证实现最大的能量,然后用这些能量护体保冲锋军平安
二分-解题突破
  • 如果第一扇门就是P,无论怎么走,谁走,能量池都将变成负数

  • 所以只有当第一扇门不是P的时候才有可能实现计划(接下来的阐述假设第一扇门不是P)

  • 最糟糕的情况就是第一扇门是V,后面全部是P

  • 那么,包括第一扇门一共len扇门,最多只要安排len-1个人就够啦

  • 所以二分的对象是人数!!!每次只要验证这个人数能否满足题意即可啦

  • 【mid变量】找人数

  • 【total变量】能量池

  • 【solve函数】判断是否可以解决题目的问题

  • 如果人数满足,那么别忘了试试更小的数,毕竟是找最少要多少人嘛(o゚▽゚)o

好啦~有思路咯,那就快来写代码叭。◕ᴗ◕。

#include<stdio.h>
#include<string>
#include<iostream>
using namespace std;
string put;
int check(int mid)
{
	int len=put.length(),total=0,before=total;
	for(int i=0;i<len;i++)
	{
		if(put[i]=='P') total--;
		else total++;//先让一个人冲锋陷阵
		if(total<0) return 0;//冲锋军阵亡
		else
		{
			if(total<=before) continue;//冲锋军正在试探ing
			else//冲锋军不辱使命找到了能量池增长的位置
			{
				total+=mid-1;
				before=total;
			}
		}
	}
	return 1;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>put;
		if(put[0]=='P') printf("-1\n");
		else//二分所需要的人数,1-put.length() 
		{
			int left=1,right=put.length(),mid;
			while(left<=right)
			{//left==right的时候还要判断是否可以check,如果不可以,还要放大一位 
				mid=(left+right)>>1; 
				//用>>1位移代替/2时间复杂度更小
				if(check(mid)) right=mid-1;
				else left=mid+1;
			}
			cout<<left<<endl;
		}
	}
	return 0;
}

实数二分

Cable master
HZNU19training题源

Background
Inhabitants of the Wonderland have decided to hold a regional programming contest. The Judging Committee has volunteered and has promised to organize the most honest contest ever. It was decided to connect computers for the contestants using a “star” topology - i.e. connect them all to a single central hub. To organize a truly honest contest, the Head of the Judging Committee has decreed to place all contestants evenly around the hub on an equal distance from it.
To buy network cables, the Judging Committee has contacted a local network solutions provider with a request to sell for them a specified number of cables with equal lengths. The Judging Committee wants the cables to be as long as possible to sit contestants as far from each other as possible.
The Cable Master of the company was assigned to the task. He knows the length of each cable in the stock up to a centimeter, and he can cut them with a centimeter precision being told the length of the pieces he must cut. However, this time, the length is not known and the Cable Master is completely puzzled.
You are to help the Cable Master, by writing a program that will determine the maximal possible length of a cable piece that can be cut from the cables in the stock, to get the specified number of pieces.

Input
The input consists of several testcases. The first line of each testcase contains two integer numbers N and K, separated by a space. N (1 ≤ N ≤ 10000) is the number of cables in the stock, and K (1 ≤ K ≤ 10000) is the number of requested pieces. The first line is followed by N lines with one number per line, that specify the length of each cable in the stock in meters. All cables are at least 1 centimeter and at most 100 kilometers in length. All lengths in the input are written with a centimeter precision, with exactly two digits after a decimal point.
The input is ended by line containing two 0’s.

Output
For each testcase write to the output the maximal length (in meters) of the pieces that Cable Master may cut from the cables in the stock to get the requested number of pieces. The number must be written with a centimeter precision, with exactly two digits after a decimal point.
If it is not possible to cut the requested number of pieces each one being at least one centimeter long, then the output must contain the single number “0.00” (without quotes).

Sample Input
4 11
8.02
7.43
4.57
5.39
0 0

Sample Output
2.00

理解-中文题意
  • 给出N条线段,以米的单位给出,小数点后两位(精确到厘米)
  • 现在要对这些线段裁剪,裁剪出K条等长的线段
  • 并且让这些线段尽可能长另外线段的长度不能小于1厘米
  • 输出要精确到小数点后两位,如果筹不够K条,输出0.00
注意-题意敲重点
  • 要精确到小数点后两位喔!!!为了完美逼近,需要开个eps精确量(如1e-5)
  • 对于输入的数据无需排序,因为得到的答案不一定是在输入的数据值里
  • 有些在输入数据中寻找答案的,则要排序后对下标进行二分
  • 初始只要记录最大值做右边界,再用其中一个数据分K段做左边界即可
  • solve函数判断记录能分的段数的时候,等于K的也要再将mid扩大尝试!!!
  • 因为答案是小数,K是整数
  • 11.09cm的线段分成11.05cm和11.08cm都是分出1段,K都是1
  • 而小数点后第二位是5还是8,就影响了题中的精确度
二分-解题突破
  • 这题思路不难,重点在于精确度

  • 只要对每单的长度进行二分预测,然后验证实际能不能分出K段这个长度

  • 如果不能分出,每段就要更小;如果可以分出,就试试更大的每段的长度能否拥有K段

  • 所以二分的对象是每段的长度!!!每次只要验证这个长度能否满足题意即可啦

  • 【mid变量】找分成多长

  • 【eps精确量】实现题意精确度,尽量开小点叭,一般1e-5/1e-6的样子,太大可能wa

  • 【solve函数】判断是否可以真的可以分mid长度分出K段

  • 如果人数满足,那么别忘了试试更大的数,毕竟是找最长每段能分出多长嘛(o゚▽゚)o

好啦~有思路咯,那就快来写代码叭。◕ᴗ◕。

#include<iostream>
using namespace std;
double put[10010];
double eps=1e-5; 
int n,k;//n是现有多少段电缆,循环输入;k是需要多少段电缆 
bool solve(double mid)
{
	long long num=0;
	for(int i=0;i<n;i++)
	{
		long long t=int(put[i]/mid);
		/*
			int()是C++的强制转换,(int)是C的强制转换 
			int()是一种单目运算符,此处运用相当于取整 
		*/ 
		num+=t;
	}
	if(num>=k) return 1;
	else return 0;
}
int main()
{
	while(~scanf("%d %d",&n,&k))//不停地输入现有段数 & 所需段数 
	{
		if(n==0&&k==0) break;
		double right=0.0;
		for(int i=0;i<n;i++)
		{//循环输入现有段数每段多长 
			scanf("%lf",&put[i]);
			if(put[i]>right) right=put[i];//right记录现有的最长段
		}
		double mid,left=put[0]/k;
		/*
			left初始记录满足第一个输入的分段长度
			因为满足所有的输入,则必然满足第一个输入
			循环二分需要初始一个左边界left
			那么left要有可能性是答案则必须满足其中一个能被分成K段
		*/
		while(right-left>eps)//1的-5次方不一定,可依据题意开 
		{//二分被切成的长度,注意精确度 
			mid=(right+left)/2;
			if(solve(mid)) left=mid;//如果处理mid发现满足题意那再去找更大的值 
			else right=mid;//不满足solve就去找更小的值 
		}//循环终止时,left和right趋近相等,进行输出
		printf("%.2f\n",left);
	}
	return 0;
}

关于二分的一些小思考…

solve函数只要验证二分到的mid能否满足题意即可
然而…left和right的变化+退出+输出应该是最头大的问题了叭

一般情况对于变化

  • 变化的时候 一般都是 left=mid+1;right=mid-1;
  • 不然会造成重复比较和死循环(整数二分)
  • 但为什么上述分线段的可以 left=mid;right=mid;
  • 因为这个题是实数二分,判断循环结束不是靠left<=right
  • 整数二分里面left永远小于right,退出循环是靠达到了预设的精确量呢

一般情况对于退出

  • 如第一道VP门题,是left<=right的循环条件,>的时候退出
  • 关键就在于这个等号
  • 因为当left和right相等的时候
  • 可能是前一个mid满足,right=mid-1;也可能是前一个mid不满足,left=mid+1
  • 总之被试solve的都是mid,相等时left和right都没被试过
  • 只有试试才知道这个新的人数能否满足solve的题意

  • 如第二道分线段题,是right-left>eps的循环条件,<=的时候退出
  • 这个就是为了足够精确,当然没必要等于啦
  • 设1e-5的eps就是希望它更小再退出嘛,达到1e-5就达到我的期望啦
  • 不然干嘛不设1e-6呢哈哈哈

注意第一题相等的时候没试过solve所以要=;第二题相等的时候精确度不够小不要=
所以一般整数二分要等号;实数二分不要等号
但具体还是得根据题意进行判断喔,免得栽在奇葩题目里哈哈哈哈哈

一般情况对于输出

  • 如第一道VP门题,输出的是left
  • 那是因为我们要找更小的那个人数
  • 满足solve的题意的时候,是right减小
  • 如果最后left=right的时候,mid通过了solve,要找更小的数
  • 那么right会减小,显然结果小于了left,不在范围内
  • 而left没变,也就是那个通过了的solve
  • 如果最后left=right的时候,mid没通过solve,要找更大的数
  • 那么right没变,显然这个值不满足题意
  • 而left增大,left>right,right变小的前提都是为了找更小的满足条件的答案
  • 大于right的范围都满足了题意,所以可以输出left

  • 如第二道分线段题,输出的是left
  • 那是因为要找分出的更长的线段
  • 满足solve的题意的时候,是left增大
  • 当即将实现right-left<=eps退出循环时,left满足solve
  • 本来还是>eps,现在要差值变小
  • 要么是mid满足solve,lef增大;要么是mid不满足solve,right减小
  • 注意整数二分,因为是整除,可能出现left==right,二分后的mid又与它们相等
  • 但是实数二分,mid出现小数位的变动一定不会与left、right相等
  • 进行变化的时候,都是left=mid,right=mid
  • 所以mid是否满足solve就代表了left或者right是否满足solve
  • 最后输出的当然得是满足solve的left啦(就算mid不满足solve,之前保存的left也依旧满足solve鸭)

注意第一题满足solve时,right减小;第二题满足solve时,left增大
第一题要找最少的人,输出left;第二题要找最大的分段值,输出left
所以通常找最少输出满足solve时和变动值相反的量;找最大输出满足solve时和变动量相同的量
但具体还是得根据题意进行判断喔,免得栽在奇葩题目里哈哈哈哈哈

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值