ACM培训2

学习总结--二分

  • 基础知识
  1. 二分查找

   前提是有序(即单调),若无序一般先sort

   向左找

   while(l

      int mid = (l+r)/2;

      if(a[mid]>=x) r=mid;

      else l=mid+1;

     }

向右找

while(l

      int mid = (l+r)/2;

      if(a[mid]<=x) l=mid;

      else r=mid-1;

     }

        懒得写时可以直接

lower_bound()  

upper_bound()

binary_search()

  1. 二分答案

 模板简单,难点是check函数的编写

 while(l<=r){

      int mid = (l+r)/2;

      if(check(mid)) l=mid+1;

      else r=mid-1;

     }

根据题目变更l与r

  • 注意点
  1. 二分答案时先确定左右区间
  2. 右边界适当大于题目给出的范围,否则有一两个测试点过不去

 

  • 写题时遇到的巧妙解法

   洛谷P8775青蛙过河

   青蛙可以跳1~L距离,求最多可以跳几次,就是取区间1~L高度和的最小值

 

 

 

洛谷P8775青蛙过河解题思路及代码

 

总体思路挺清晰的,将1到n区间二分答案,难点在于如何写check函数,分析可知从河左边与右边走是一样的,即只要青蛙可以过河的次数大于等于2x就行,青蛙可以跳1~mid的距离,要保证青蛙在此区间内每一次都可以有落脚点,就是取mid区间内高度和最小值,即取最小前缀和,至此思路完整

 

AC代码:

#include

using namespace std;

int n;

long long x;

long long a[100001];

bool check(int mid){

long long sum=0,minn=1e10;

for(int i=1;i

for(int i=mid;i

sum+=a[i];

minn=min(sum,minn);

sum-=a[i-mid+1];

}

if(minn>=2*x) return 1;

else return 0;

}

int main(){

cin>>n>>x;

for(int i=1;i>a[i];

int l=1,r=n,ans;

while(l<=r){

int mid=(l+r)/2;

if(check(mid)) ans=mid,r=mid-1;

else l=mid+1;

}

cout<

return 0;

}

 

 

 

 

 

 

 

 

洛谷P1281书的复制解题思路及代码

 

总体思路还是二分答案,代码可以分为三部分,check二分和划分每个人抄写范围。首先明确二分区间,l为1,r为复制有书的总时间,接下来写check函数,比较恶心的一点是输出让前面的人尽可能少写,所以写check时得从后往前遍历,用count来记录按不超过mid的时间需要几人,若count<=题目给定人数,往左找,大于往右找,最后再根据所求时间从后往前进行划分,输出,思路完整,按思路和题目模拟就行

 

   AC代码:

   #include

using namespace std;

long long sum=0;

int n,k,a[502],b[502][2];

bool check(int mid){

long long sum2=0;

int count=0;

for(int i=n;i>=1;i--){

sum2+=a[i];

if(sum2>mid&&i!=1){

sum2=a[i];

count++;

}

if(i==1){

if(sum2>mid) count+=2;

else count++;

}

}

if(count<=k) return 1;

else return 0;

}

int main(){

cin>>n>>k;

for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];

int l=1,r=sum,ans;

while(l<=r){

int mid=(l+r)/2;

if(check(mid)) ans=mid,r=mid-1;

else l=mid+1;

}

int num=k,y=n,sum2=0;

for(int i=n;i>=1;i--){

sum2+=a[i];

if(sum2>ans&&i!=1){

b[num][0]=i+1;

b[num][1]=y;

sum2=a[i];

num--;

y=i;

}

if(i==1&&sum2>ans){

b[2][0]=2,b[2][1]=y,b[1][0]=1,b[1][1]=1;

}else{

b[1][0]=1,b[1][1]=y;

}

}

for(int i=1;i<=k;i++) cout<

return 0;

}

 

 

 

 

 

洛谷P8800卡牌解题思路及代码

 

二分,第一明确查找区间即1~大于最大卡牌数的任意数(尽量开大),接下来作些准备工作(个人做法),先贪心,对记录数据的结构体排个序,可以节省check的循环次数,接着是关键的check,从最少的牌数开始遍历,与套数mid进行比较,若小于mid,判断能否补到mid,可以,continue,否则直接break,check结束,若大于等于因为已经排好序后面肯定也大于等于check结束,

 

AC代码

#include

using namespace std;

struct pai{

int a;

int b;

};

struct pai c[200002];

long long n,m;

bool cmp(pai s1,pai s2){

return s1.a

}

bool check(int mid){

long long sum=m,flag=2;

for(int i=1;i<=n;i++){

if(c[i].a

if(c[i].b>=mid-c[i].a&&sum>=mid-c[i].a){

sum=sum+c[i].a-mid;

if(i==n) flag=1;

}else{

flag=0;

break;

}

}else{

flag=1;

break;

}

}

if(flag==0) return 0;

else if(flag==1) return 1;

}

int main(){

cin>>n>>m;

for(int i=1;i<=n;i++) cin>>c[i].a;

for(int i=1;i<=n;i++) cin>>c[i].b;

sort(c+1,c+n+1,cmp);

int l=1,r=2e6,ans;

while(l<=r){

int mid=(l+r)/2;

if(check(mid)) ans=mid,l=mid+1;

else r=mid-1;

}

cout<

return 0;

}

 

 

 

 

 

洛谷P8647分巧克力解题思路及代码

 

题单里最简单的二分查找,套资料模板就行,直接上AC代码

 

#include

using namespace std;

int a[100001][2],n,k;

bool check(int mid){

int cnt=0;

for(int i=1;i<=n;i++) cnt+=(a[i][0]/mid)*(a[i][1]/mid);

if(cnt>=k) return 1;

else return 0;

}

int main(){

cin>>n>>k;

for(int i=1;i<=n;i++) cin>>a[i][0]>>a[i][1];

int l=1,r=1e5,ans;

while(l<=r){

int mid=(l+r)/2;

if(check(mid)) ans=mid,l=mid+1;

else r=mid-1;

}

cout<

return 0;

}

 

 

 

 

 

洛谷P1102A-B数对解题思路及代码

 

三个月前写的,现在只记得大致思路,二分查找,第一步sort排序,然后两次二分查找,找小于等于a[i]-C的和小于a[i]+C的,最后用sum累加和

 

AC代码

 

#include

using namespace std;

const long long y=1000010;

long long w[y],n,m,mm,nn,sum=0;

int main(){

cin>>n>>m;

for(int i=1;i<=n;i++){

cin>>w[i];

}

sort(w+1,w+1+n);

for(int i=1;i<=n;i++){

int l=1,r=n;

while(l

int mid=(l+r)/2;

if(w[mid]-w[i]>=m){

r=mid;

}else{

l=mid+1;

}

}

if(w[l]-w[i]==m){

mm=l;

}else{

continue;

}

l=mm-1,r=n;

while(l

int mid=(l+r+1)/2;

if(w[mid]-w[i]<=m){

l=mid;

}else{

r=mid-1;

}

}

if(w[l]-w[i]==m){

nn=l;

}else{

continue;

}

sum+=nn-mm+1;

}

cout<

return 0;

}

 

 

 

51Nod-2063二分查找解题思路及代码

 

水题,套模板或直接binary_search(),上代码

 

#include

using namespace std;

long long a[100002];

int n,q;

int main(){

cin>>n;

for(int i=1;i<=n;i++) cin>>a[i];

cin>>q;

for(int i=1;i<=q;i++){

long long x;

cin>>x;

if(binary_search(a+1,a+n+1,x)){

cout<<"Yes"<

}else{

cout<<"No"<

}

}

return 0;

}

 

 

 

 

你可能感兴趣的:(算法,笔记)