【尺取+二分+三分总结】2019 GDUT Winter Training I
本文同步发布于个人blog,点此访问
寒假集训专题一(对没错本蒟蒻来CtrlCV大法写题解+总结了)
专题一(点这里)包括尺取,二分,三分,dfs,bfs(搜索好难_(:з」∠)_)
这篇文字就简单讲讲尺取、二分、三分吧
尺取
基本思路:
尺取也被称为“毛毛虫法”,顾名思义,在求一段连续子区间时,左右边界进行挪动从而得到解。
(这不是废话嘛谁知道你在讲什么)那么接下来从例子入手。
例题一(A题):大意是给一段n个正整数的序列,让你找出最短的满足区间和大于S的子序列的长度
对于这道题,我们可以考虑维护l、r表示当前区间的边界,初始l,r指向序列头,对于当前区间,
如果区间和仍小于S,那么我们需要使r往前移(就像毛毛虫的头往前挪),直到当前区间和大于
等于S,记录下当前区间长度这时候如果r继续前移,那么虽然当前区间满足条件,但长度必定比
已经记录的值更长,所以我们需要l前移(毛毛虫的尾巴)。重复上述过程,直到“毛毛虫”爬完整个序列。
代码如下
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; const int maxn = 1e5+10; int T,n,s,num[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&n,&s); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } int l=1,r=1,sum=num[1],ans=maxn; while(l<=r&&r<=n) { //cout<<sum<<endl; if(sum>=s) { if(sum-num[l]>=s) sum-=num[l++]; else{ //cout<<l<<" "<<r<<endl; ans=min(r-l+1,ans); sum-=num[l++]; } }else{ 4000 sum+=num[++r]; } } if(ans==maxn) cout<<0<<endl; else cout<<ans<<endl; } return 0; }
例题二(B题):大意是求1-n中哪一段子区间的平方和等于S。按字典序输出全部情况。
基本思路和例题一类似,唯一不同的是需要输出序列,可以考虑用数组记录下l,r,输出答案
时再输出即可。
那就直接贴代码了(只是懒得再写一遍题解):
#include <cstdio> #include <cmath> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; const int maxn = 1e7; ll n,x[maxn],y[maxn],ans=0; int main() { scanf("%lld",&n); ll l=1,r=1,sum=1,len=sqrt(n)+2; while(l<=r&&r<=len) { //cout<<sum<<endl; if(sum>=n) { if(sum==n) { ++ans; x[ans]=l,y[ans]=r; } sum-=l*l; l++; }else{ r++; sum+=r*r; } } cout<<ans<<endl; for(int i=1;i<=ans;i++) { cout<<y[i]-x[i]+1<<" "; for(int j=x[i];j<=y[i];j++) { cout<<j<<" "; } cout<<endl; } return 0; }
小结:尺取可以在O(n)的时间得出类似的求一段连续子区间的解,虽然
适用性不广(至少在我看来,也可能是因为我菜QAQ),不过该用上的时候也会
有不错的效果。
二分
基本思路:二分的思想最早可以从中学学过的牛顿迭代求根开始接触到,
简单来说,假如要在一段单调性唯一(假定是单增)的序列中找到一个值,维护区间边界l,r,
每次取l,r中点值与需要找的值作比较,若过大,则右边界左移至中点处,反之则左边界右移
到中点处,如此往复,那么在每次折半后,最多只需要log2(n)次便可以检索到解。
下面三道例题:
例题三(C题):大意是给出n个位置,选出其中m个位置,使得m个位置之间的最短距离
最大。输出最大的最短距离。
这道题可以对距离进行二分,首先对位置排序,对于每个距离,如果能满足可以选择的位置
大于等于m个,那么该距离为合法距离。考虑维护区间边界l,r,中点mid,如果mid为合法距离
那么区间向右移,如果mid不合法,那么区间应该往左移才能得到合法距离。
具体看代码叭:
#include <cstdio> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1e9+5; int num[100000+5],n,c; int check(int o) { int u=1,now=1; for(int i=2;i<=n;i++) { while(i<=n&&num[i]-num[now]<o) { i++; } if(i<=n) { now=i; u++; } } if(u>=c) { return 1; }else{ return 0; } } int main() { scanf("%d %d",&n,&c); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } sort(num+1,num+n+1); int l=0,r=maxn; while(l<r-1) { int mid=(l+r)/2; //cout<<l<<" "<<mid<<" "<<r<<endl; if(check(mid)) { l=mid; }else{ r=mid; } } if(check(l))printf("%d\n",l); else printf("%d\n",r); return 0; }
例题四(D题):一根棍子横立与两面墙之间,当棍子受热膨胀后会弯成弓形(可以看做
圆弧的一部分),求弯曲的棍子与原棍子的中点的距离。
题目思路不难想,设长度为x,那么根据勾股定理和三角函数就可以列出一系列方程,
那么只要对x进行二分,也就是前文提到的牛顿迭代求解的过程,就可以得到解。
(然而这道题我交了30发的WA,直到我把评测姬从g++改成c++,wa的一声哭出来)
代码如下:
#include <cstdio> #include <string.h> #include <cmath> #include <iostream> #include <algorithm> using namespace std; #define eps 1e-6 double L,LL,n,c; int main() { while(~scanf("%lf %lf %lf",&L,&n,&c)) { if(L<0&&n<0&&c<0) break; LL=L*(1.0+n*c); double R,l=0.0,r=L*0.5,mid; while(r-l>eps) { mid=(l+r)/2.0; if(2 * asin((L / 2) / ((L*L + 4 * mid*mid) / (8 * mid)))*((L*L + 4 * mid*mid) / (8 * mid))>=LL) { r=mid; }else{ l=mid; } } printf("%.3lf\n",mid); } return 0; }
例题五(F题):简单二分+交互,具体看原题
CF的特有题,交互很有意思一般也不难,加上fflush(stdout)或者用
endl就问题不大了,没看清题可能会吃亏,(手动打码)我才不会告诉你这道题
我看错了大于小于号WA了近十发。(专题感觉像划水一样随随便便就交题了太不认真了orz)
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 1e9; int main() { int l=0,r=1e9,mid; while(1) { mid=(l+r)/2; cout<<"Q "<<mid<<endl; string c; cin>>c; cout.flush(); if(c=="=") exit(0); else if(c=="<") { r=mid; }else { l=mid+1; } } return 0; }
小结:二分作为一个常用算法,写起来还不是特别顺手,以后打cf听师兄
说中间大部分都是二分题啊。
三分
基本思路:三分的思路和二分基本相似,不一样的是,二分求解的是单调序列的问题,而三分
求解的是一段有极值的序列。三分,顾名思义,在二分一个mid的基础上,取mid和r的中点为
mid2,通过对mid和mid2对应的值的比较,确定是否左右移,从而求解。
例题六(E题):大意是给出一系列二次函数,取F(x)=max(s[i](x)),x∈[1,n].求F(x)
的最小值,x属于[0,MAXN].
简单三分,话不多说,直接上代码叭
代码:
#include <iostream> #include <algorithm> #include <cstdio> #include <string.h> #include <cmath> using namespace std; const int maxn=1e4+5; int T,n,a[maxn],b[maxn],c[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %d %d",&a[i],&b[i],&c[i]); } double l=0,r=1000,mid1,mid2,now1,now2; while(r-l>0.00000000001) { mid1=(l+r)/2; mid2=(mid1+r)/2; now1=a[1]*mid1*mid1+b[1]*mid1+c[1]; now2=a[1]*mid2*mid2+b[1]*mid2+c[1]; for(int i=2;i<=n;i++) { now1=max(now1,a[i]*mid1*mid1+b[i]*mid1+c[i]); now2=max(now2,a[i]*mid2*mid2+b[i]*mid2+c[i]); } //printf("%lf-%lf:%lf=%lf %lf=%lf\n",l,r,mid1,now1,mid2,now2); if(now1>now2) { l=mid1; }else{ r=mid2; } } printf("%.4lf\n",now1); } return 0; }
后记:(第一篇总结报告写得像翔一样我也没有办法鸭_(:з」∠)_)
- 二分,三分总结
- 2019 GDUT Spring Training IV (Div.2) (dp,)
- SDKD 2016 Winter Single Training B, div2 出题总结
- 二分三分总结(需要注意点)
- 二分、三分总结
- 【二分】Petrozavodsk Winter Training Camp 2017 Day 1: Jagiellonian U Contest, Monday, January 30, 2017 Problem A. The Catcher in the Rye
- Tri_integral Winter Training 0 比赛总结
- 二分和三分总结与误区分析
- 【动态规划】【二分】Petrozavodsk Winter Training Camp 2017 Day 1: Jagiellonian U Contest, Monday, January 30, 2017 Problem B. Dissertation
- 2019 HZNU Winter Training Day 13 Comprehensive Training
- 2019 HZNU Winter Training Day 15 Comprehensive Training
- uva 1463 - Largest Empty Circle on a Segment(二分+三分+几何)
- hdu 5289 Assignment 二分+RMQ 2015 Multi-University Training Contest 1 02
- HDU 2899 (二分 或者 三分)
- 二分查找各种情况大总结
- 二分总结一 二分法试解 POJ1064
- BUPT Spring training 11th 总结
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- 二分匹配总结(匈牙利算法+最大权+最小权)
- 2015-2016 Petrozavodsk Winter Training Camp, Nizhny Novgorod SU Contest (5/9)