二分YYDS;
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if ("条件") r = mid;
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1>> 1;
if ("条件") l = mid;
else r = mid - 1;
}
return l;
}
由于l+r>>1 是向下取整
当区间只有两个数的时候,mid=1,bsearch_2造成死循环。
--------------------------------------------------------------------------------------------------------------------分割线
最近算是重新回顾了一下二分这个算法吧。
然后我发现我之前学的二分算是学了个寂寞,这次温故而知新,感觉收获还是蛮大的,所以,我来更新博客了。
上次的话,咱们是就直接放了模板在这,这次咱们来看看为啥要用两个板子。
一个板子不行吗?
先来放上练习题:
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;
int n,m;
LL a[N];
LL bsearch(LL l,LL r,LL x)
{
while(l<r)
{
LL mid = l+r>>1;
if(a[mid] >= x) r = mid;
else l = mid+1;
}
return l;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
while(m--)
{
LL x;
cin>>x;
LL t = bsearch(1,n,x);
if(a[t]!=x) cout<<-1<<" ";
else cout<<t<<" ";
}
return 0;
}
A-B 数对 - 洛谷(这道题我貌似是用哈希表写的[doge])
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
typedef long long LL;
LL n,c;
LL a[N],idx;
bool st[N];
LL res;
unordered_map<LL,LL>heap;
int main()
{
scanf("%lld%lld",&n,&c);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
heap[a[i]]++;
a[i] -=c;
}
for(LL i =1;i<=n;i++)
{
res +=heap[a[i]];
}
cout<<res<<endl;
return 0;
}
[COCI 2011/2012 #5] EKO / 砍树 - 洛谷
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;
LL n,m;
LL a[N];
LL sum(LL y)
{
LL res=0;
for(LL i =1;i<=n;i++)
{
LL x = a[i];
if(x>y) res += x - y;
}
return res;
}
LL bsearch(LL l,LL r,LL x)
{
while(l<r)
{
LL mid = l + r + 1>>1;
LL t = sum(mid);
if(t == x)return mid;
else if(t>x) l = mid;
else r = mid - 1;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(LL i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1);
cout<<bsearch(a[1],a[n],m);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;
int n,k;
int a[N];
int MX;
int sum(int mid)
{
int res =0;
for(int i=1;i<=n;i++) res += a[i]/mid;
return res;
}
int search(int l,int r)
{
while(l<r)
{
int mid = l + r + 1>>1;
if(sum(mid)>=k) l = mid;
else r = mid -1;
}
return l;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
MX = max(MX,a[i]);
}
if(MX ==0)cout<<0<<endl;
else cout<<search(0,MX)<<endl;
return 0;
}
咱们来看看这两个板子的区别:
bsearch_1板子:将区间划分成了 [ l , mid ] 和 [ mid + 1 , r];
bsearch_2板子:将区间划分成了 [ l , mid - 1 ] 和 [ mid , r];
垂死梦中惊坐起,再看俩板我可以。
我们的"条件"是查找出满足我们条件的答案的。
但是如果当答案不为一时呢,这时候两个板子的作用就展现出来了。
我们来看看这串数字,然后我们来找出 2 这个数的位置。
我们用第一个板子来找,可以惊奇的发现,我们找到的是第一个 2 出现的位置,而用第二个板子可以找到最后一个 2 出现的位置。
这就是这两个板子的巧妙之处,具体的东西还需要大家亲手去写一下,模拟一下过程的。
----------------------------------------------------------------------------------------------------- -这是一条分割线
我又来了,最近又学习了一遍二分,只能说二分YYDS了;
其实上面的东西简单来说就是,二分查找符合条件的两个边界;
也就是左右边界,这次学了一个新的二分写法,大家感兴趣的可以看看;
这次我把之前放的练习题的代码也放上了(不知道为啥浏览好少);思路啥的我都不记得了;
这个题的话,我们就二分答案,然后枚举一下,查看当前答案是否合法,(枚举的过程我用的方法可能有点傻),然后我们根据结果进行二分的边界选择;
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#define x first
#define y second
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 50010;
int k,m,n;
LL a[N],s[N];
bool check(LL num)
{
LL ant = 0, i = 1;
while(i <= n)
{
if(s[i] < num)
{
LL sa = s[i],j = i + 1;
while(sa < num && j <= n + 1)
{
ant++;
sa += s[j];
j++;
}
i = j - 1;
}
i ++;
}
if(ant > m) return false;
return true;
}
int main()
{
scanf("%d%d%d",&k,&n,&m);
for(int i =1 ;i<= n;i++)
{
scanf("%lld",&a[i]);
s[i] = a[i] - a[i-1];
}
s[m+1] = k - a[n];
LL l = 0 , r = k + 1;
while(l + 1 < r)
{
LL mid = l + r >> 1;
if(check(mid)) l = mid;
else r = mid ;
}
printf("%lld\n",l);
return 0;
}
本题和上一题基本一样,我们二分答案,对于每一个路灯区间,我们查看使其合法化需要多少个路灯,最后求和题目给出的对比,然后判断二分边界,注意本题查找的是左边界。
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#define x first
#define y second
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 100010;
LL ll,n,k;
int a[N],s[N],idx;
bool check(LL num)
{
LL ant = 0;
_for(i,1,idx)
{
if(s[i] > num)
{
ant += (s[i] / num) - 1;
if(s[i] % num) ant ++;
}
}
if(ant > k) return false;
return true;
}
int main()
{
scanf("%lld%d%d",&ll,&n,&k);
_for(i,1,n) scanf("%lld",&a[i]);
_for(i,2,n) s[++idx] = a[i] - a[i-1];
LL l = 0, r = ll;
while(l < r)
{
LL mid = l + r >> 1;
if(check(mid))r = mid;
else l = mid + 1;
}
printf("%lld\n",l);
return 0;
}
/*
101 5 1
0 38 69 83 101
31
*/