该篇博客,以及后面的标题类似的博客用于记录洛谷的作业。
贴一下一个同学分享的博客,就是看这个学的二分:二分查找 & 二分答案 万字详解,超多例题,带你学透二分。_二分法例题及其答案_小酒窝.的博客-CSDN博客
1.P8682 等差数列
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b)
{
if (a%b==0) return b;
else return gcd(b, a % b);
}
int main()
{
int n; cin>>n;
int arr[100005];
for(int i=0;i<n;i++) cin>>arr[i];
sort(arr,arr+n);
if(arr[0]==arr[n-1]) {cout<<n; return 0;}
int d=arr[1]-arr[0];
for(int i=2;i<n;i++) d=gcd(d,arr[i]-arr[i-1]);
cout<<(arr[n-1]-arr[0])/d+1;
return 0;
}
思路:先排序,然后在各项之差中用循环找最大公约数,最大公约数用辗转相除法求得。最后用数列中最大项与最小项之差除以最大公差加一即为项数。
2.P1226 快速幂
#include<iostream>
using namespace std;
long long quickpower(long long a,long long b,long long c){
long long ans=1;
while(b){
if(b&1){
ans=(ans*a)%c;
}
a=(a*a)%c;
b/=2;
}
return ans;
}
int main(){
long long a,b,c;
cin>>a>>b>>c;
cout<<a<<"^"<<b<<" mod "<<c<<"="<<quickpower(a,b,c);
return 0;
}
这个是网上找视频看的,基本算是抄的。写一下抄来的思路:
b&1相当于b%2==1,b>>=1相当于b/=2
还会用到取模运算的性质
快速幂是一种将底数平方,指数减半的简化运算的方法,对指数是奇/偶存在各自的处理方式,本题又多了一个求模,所以可以在简化运算的同时运用(ab)%p=(a%p)(b%p)%p,嗯,就这样。
3.P2249 二分查找
二分啊二分,好难好难,头秃好几天,到现在也没太搞清楚。这道题是自己上网找完视频也不太懂,然后边对着答案改自己敲好的写完的。基本全是抄的。
#include<iostream>
using namespace std;
int main(){
int n,m; cin>>n>>m;
//int l=1,r=n,k; 为啥放在这里就过不了?
int a[1000010];
for(int i=1;i<=n;i++) cin>>a[i];
while(m--){
int l=1,r=n,k;
cin>>k;
while(l<r){
int mid=l+r>>1;
if(a[mid]>=k) r=mid;
else l=mid+1;
}
if(a[l]!=k){
cout<<"-1 ";
continue;
}
else cout<<l<<' ';
}
return 0;
}
写一下我对抄来的这个思路的理解:
二分法就是通过移动左/右边界值使得要查找的区间不断减半,最终确定所求值。现在觉得这个题挺简单的,纯粹的二分题。后面的二分答案的那种题就感觉有点抽象,难以理解。
有同学回答了注释里的问题,也贴上来吧。
4.P1824 进击的奶牛-二分答案
看了好久讲二分的博客,好难好难,看一遍,自己敲一遍,对着改。然后重复。也没写几个题。
这道题类似于博客里讲的跳石头和丢瓶盖,所以按照他们的思路自己敲了一下。感觉只是换了个皮,所以也基本就是抄的了(笑哭,四个题抄了三个半)。但是也算是第一道自己AC的算法题吧,别的多少都在看直接的答案,这个起码看的是别的题的答案然后触类旁通(虽然两个题很像很像)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
long long n,c,a[N];
int check(int mid){
int i=1,now=1,num=0;
while(i<n){
i++;
if(a[i]-a[now]>=mid) {now=i; num++;}
}
if(num+1>=c) return 1;
return 0;
}
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
int l=0,r=a[n];
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}
思路:求两头牛中间最小距离的最大值,那么按照博客里一直强调的,......就该>=mid,先对坐标进行排序,然后确定左右边界值,写好二分的模板(也是从博客学的)后,就该完善check函数了。
牛牛的间距,自然应该用坐标差来表示,既然有差,就要有两个变量i和now,还要有一个计数的num(表示加入一段间隔,所以后面用num+1表示牛的头数)。
想象一下,地上一列灯,要每隔一定间距点亮一盏,第一盏已亮。一个人从第一盏开始往后走,自然是走出至少为mid的距离时才点亮下一盏灯,这中间略过的灯就是跳石头那道题移走的石头,这些点亮的灯,就是丢瓶盖那道题里拿出的瓶盖,和进击的奶牛这道题里放置愤怒牛牛的牛棚。当间距满足>=mid后,num就可以+1,此时now=i,即为从该盏灯开始继续进行下一次的判定:走出>=mid的距离后,再次点亮第三盏灯。以此类推,如果走完全程,放红温牛牛的棚子(num+1,不是num)大于c,也就是说放得下,那就return 1,再次进行判定,直到num+1==c时,此时执行语句r=mid-1,但输出的是l,所以就可以输出对应num+1=c时的l值,这个值就是通过二分找出来的最小间距的最大值。
感悟:
二分答案往往需要处理两个东西,第一个是最值,第二个是二分。
最值的处理,就是check(mid)函数中的内容,这部分往往需要用到题中的距离、数目等等等等花里胡哨的数目(设为c),就是你的check函数要根据主函数中二分模板给出的mid值,把题给的一大堆数据处理出若干个结果或者组(设为num),将num与c比较,然后给check函数设置对应的返回值,再进行二分。从而不断逼近要找的值。然后cout就可以了。
掌握的极其差劲,还要再写写过的题。我不是那种天赋异禀的人,先保证做一道能理解一道吧。
贴一下那个博客里二分的模板:
感想:
别人在9028看视频,手搓算法题。我在9028搜集好几个答案,然后给自己写的加点这个改点那个。别人在学习,我在做实验,哈哈哈哈。我这个为什么错了,我的程序加上答案的什么就可以对了?我的什么地方是对的,答案里的什么替换成我写的也是可以过的?这周大概就是这样的思路了,所以一个题有可能会交几十遍,因为一直在试怎么可以过,怎样过不了。
Acm好难啊!!慢慢来吧,加油加油。