二分、三分、01分数规划 【第I弹】

目录

一、二分

1.对不同板子的理解

例题一:

超级经典二分题!!!Drying !


一、二分

1.对不同板子的理解

【听说二分很容易写歪,细节要注意,接下来的几种写法,建议是选中一个自己熟悉的就一直只写这个,免得写混,不过还是要好好理解】

 要避免“死循环”的出现,就是要让每一次的执行都有所改变,而不是判断之后相当于什么也没做,具体到代码就是 l 和 r 要移动,那为什么会死循环呢?因为我们的 mid = ( l + r )/2是“整除”啊,是“向下取整”,当 r==l+1 时,mid 会等于其中较小的那一个,也就是 l ,这时候如果右边的代码也写成  mid = ( l + r )/2 ,那么会在 l = mid 这里进入死循环(把 l 赋值为 l ,你干了个啥.)【可以拿数列 3 3 3 做下模拟】关于最终答案:找等号嘛,最后一次执行的mid就是x的下标啦,然后左边的就是 r ,右边的就是 l.

再来看一类写法:直接舍弃 mid ,不考虑那么多,每次 l = mid + 1 , r = mid - 1 , 那么区间就一定是在缩小的:

注意啦!这里while的条件里要等号

 那,我们最终答案写什么,看等号,对于上图左边的代码,if ( a[mid]>= x) r = mid - 1 ;最终 mid 是答案 所以 我们输出的就是 r + 1, 或者可以看下面那一句,a[mid] < x 时, l = mid +1 ,这里最后一次的mid 小于 x ,那就是比x小1,所以答案也是 l , 右边就是 l - 1 ,或者 r

还有就是,(l+r)有一种更安全的写法来防止溢出的:l + (r-l)/2 ;

 lower_bound是找大于等于x的第一个数的位置,upper_bound可用来找大于x的第一个数,记得减去数组名(因为返回的是迭代器(类似指针),减去数组名(首地址)就得到下标了)。。

【上面  就算排序下标从1开始,后面也不要多减一...因为你减的是地址得到一个你现在的下标,本来就是从多一的开始了,自然没必要减啊。。】

例题一:

A-[USACO 2009 Dec S]Music Notes_

题意:牛依次敲(奏)n个音符,给出每个音符分别要(连续)弹奏多长时间,Q次询问,每次询问给个T,问T~T+1时间牛牛们要敲哪个音。

思路:一个前缀和,然后标准二分思路,都不用自己写了,用现成函数就好。

AC代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int M=5e4+6;
int n,q,a[M];
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>q;
	int i,x,t;
	for(i=1;i<=n;i++){
		cin>>x;
		a[i]=a[i-1]+x;
	}
	while(q--){
		cin>>t;
		cout<<upper_bound(a+1,a+1+n,t)-a<<'\n';
	}
	return 0;
}

超级经典二分题!!!Drying !

Drying - POJ 3104 - Virtual Judge (vjudge.net)

题意:有 n 件衣服,每件衣服含有一定水分,有自然晾干和拿吹风机吹干两种方式,晾干是每分钟减少一单元水,吹风机是每分钟使其减少 k 份水(这一分钟只能吹一件,不能换另一件衣服吹,衣服水分变为0后不会有变化)问最少多少分钟能把衣服全部弄干。

思路:这题满足单调性,即可二分结果,假设x时间为答案,那么比答案大的(判断的是"mid"时间),计算出的要花的时间就会比你预想的时间mid 少(即有时间剩余,所以我们这个答案大了,就该缩小,区间右边界左移,反之亦然)

虽然说这里的k是包含了自然风干的水分的(即这段时间干的就是k不是k+1)但千万注意这里除的是 (k-1),www为什么呢,咱就是说凡事推推公式 好处多多 一目了然,不要想当然。->设总时间为x , t 为吹风机时间,kt + (x-t) >=a[i]  , kt-t >= a[i]-x , (k-1)t >=a[i]

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int M=1e5+6;
ll n, k, a[M], sum;
bool check(int x){
	int i=upper_bound(a+1,a+1+n,x)-a;//找到第一个需要使用吹风机的位置 
	sum=0;
	for(;i<=n;i++)sum+=(a[i]-x+k-2)/(k-1);//加个k-2是为了向上取整,也可以用ceil函数 
	return sum<=x?1:0;//计算的时间比设定的小,就返回真 
}
int main(){
	scanf("%lld",&n);//POJ卡cin.... 
	for(int i=1;i<=n;i++)scanf("%lld",a+i);
	sort(a+1,a+1+n);
	scanf("%lld",&k);
	if(k==1){printf("%lld",a[n]);return 0;}
	int l=1, r=1e9;
	while(l<r){//二分板子之一,不熟悉就看看上文吧 
		int mid=l+r>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	printf("%d",r);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值