二分法练习1
poj3273 Monthly Expense (二分,最大值最小化)
题意:将N个账款分割成M个财务期,使得每个分期账款和的最大值最小。
题解:贪心思想,二分法。上界为N天花费总和,下界为每天花费的最大值。根据mid值遍历n天花费看是否满足M各财务期条件
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int main(){ 5 int n,m,i,cnt,a[100001],ma=0,s=0; 6 scanf("%d%d",&n,&m); 7 for(i=0;i<n;++i){ 8 scanf("%d",&a[i]); 9 ma=max(a[i],ma); 10 s+=a[i]; 11 } 12 int l=ma,r=s,mid; 13 while(l<r){ 14 mid=(l+r)>>1; 15 cnt=1; 16 for(s=i=0;i<n;++i){ 17 s+=a[i]; 18 if(s>mid){cnt++;s=a[i];} 19 } 20 if(cnt<=m) r=mid; 21 else l=mid+1; 22 } 23 printf("%d\n",l); 24 return 0; 25 }
poj3258 River Hopscotch (二分,最小值最大化)
题意:一条长为L的河,除了起始点还有N个石子,分别距离起点Di,求去掉M个石子后相邻石子最小距离的最大值。
题解:上界为河长L,下界为初始时两相邻石子间最小距离,根据mid值遍历N个石子看是否满足移除M石子的条件。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int main(){ 5 int L,n,m,i,l,r,mid,cnt,s,a[50002]; 6 scanf("%d%d%d",&L,&n,&m); 7 for(i=1;i<=n;++i) scanf("%d",&a[i]); 8 a[0]=0; 9 sort(a,a+n+1); 10 l=r=L; 11 for(i=1;i<=n;++i) l=min(l,a[i]-a[i-1]); 12 while(l<r){ 13 mid=(l+r+1)/2;//注意+1保证left每次都增大 14 cnt=s=0; 15 for(i=1;i<=n;++i){ 16 s+=(a[i]-a[i-1]); 17 if(s<mid) cnt++; 18 else s=0; 19 } 20 if(cnt<=m) l=mid; 21 else r=mid-1; 22 } 23 printf("%d\n",l); 24 return 0; 25 }
poj1905 Expanding Rods (二分,几何)
题意:一根棍子,受热后长度会变为L' = (1+n*C)*L(棍子变为圆弧)。问受热后棍子的中点距离地面的高度h为多少。
题解:做这题我复习了一下相交弦定理(初中学的都快忘了):若AB是直径,CD垂直AB于点P,则 =PA·PB。
直接二分高度,算出半径和弧度,然后将算出的弧长与实际弧长比较。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #define eps 1e-4 5 using namespace std; 6 int main(){ 7 double L,n,c,L1,sita,l,r,mid,R; 8 while(scanf("%lf%lf%lf",&L,&n,&c)==3){ 9 if(L<0||n<0||c<0)break; 10 L1=(1+n*c)*L; 11 l=0; r=L/2; 12 while(l+eps<r){ 13 mid=(l+r)/2; 14 R=L*L/mid/8+mid/2;//相交弦定理 15 sita=2*asin(L/2/R); 16 if(sita*R<=L1) l=mid; 17 else r=mid; 18 } 19 printf("%.3f\n",l); 20 } 21 return 0; 22 }
poj3122 Pie (二分)
题意:有n块高度为1,半径不等的圆柱形披萨,要将其“平均”(每份体积相等,形状无要求,但必须每份是从同一个披萨上得到的)分给(f+1)人。
题解:下界为0,上界为最大的披萨的尺寸,二分尺寸看能分给多少人。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #define pi 3.14159265359//背一下咯 5 #define eps 1e-6 6 using namespace std; 7 int main(){ 8 int t,i,n,f,a[10001],R,cnt; 9 double l,r,mid; 10 scanf("%d",&t); 11 while(t--){ 12 scanf("%d%d",&n,&f); 13 f++;//总人数记得加上自己 14 l=r=0; 15 for(i=0;i<n;++i){ 16 scanf("%d",&R); 17 a[i]=R*R; 18 r=max(1.*a[i],r); 19 } 20 while(l+eps<r){ 21 mid=(l+r)/2; 22 cnt=0; 23 for(i=0;i<n;++i) 24 cnt+=(int)(a[i]/mid); 25 if(cnt>=f) l=mid; 26 else r=mid; 27 } 28 printf("%.4f\n",(double)mid*pi); 29 } 30 return 0; 31 }
poj2413 How many Fibs? (高精度,二分)
题意:定义fibonacci数列前两项f[1] = 1,f[2] = 2。现在给你一个区间[a,b],a <= b <= 10^100。问:区间[a,b]之间有多少个fibonacci数。
题解:先算出480个斐波那契数(第480个斐波那契数是101位了)。二维数组F[i][]表示第i个斐波那契数,二分查找a,b位置,如果找到的位置不是斐波那契数,则返回第一个比它大的斐波那契数的位置,注意对b判断作标记。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int N=482; 7 const int M=102; 8 int F[N][M],f; 9 char Fi[N][M],a[M],b[M]; 10 void Fib(){ 11 int i,j,k; 12 F[1][0]=1;F[2][0]=2; 13 for(i=3;i<N;++i){ 14 for(j=0;j<M;++j){ 15 F[i][j]+=F[i-1][j]+F[i-2][j]; 16 if(F[i][j]>=10){ 17 F[i][j+1]+=F[i][j]/10; 18 F[i][j]%=10; 19 } 20 } 21 } 22 for(i=1;i<N;++i){ 23 for(j=M-1;j>=0;--j){//清除前导0 24 if(F[i][j]==0)continue; 25 else break; 26 } 27 k=0; 28 while(j>=0){ 29 Fi[i][k++]=F[i][j]+'0'; 30 j--; 31 } 32 Fi[i][k]='\0'; 33 } 34 } 35 int cmp(char*a,char*b){ 36 int La=strlen(a); 37 int Lb=strlen(b); 38 if(La!=Lb) return La<Lb?-1:1; 39 else return strcmp(a,b); 40 } 41 int bi_search(char *s){ 42 int l=1,r=480,mid; 43 while(l<=r){ 44 mid=(l+r)/2; 45 int x=cmp(s,Fi[mid]); 46 if(x==0){ 47 f=1; 48 return mid; 49 } 50 else if(x<0) 51 r=mid-1; 52 else l=mid+1; 53 } 54 return l; 55 } 56 int main(){ 57 Fib(); 58 int l,r; 59 while(~scanf("%s %s",a,b)){ 60 if(strcmp(a,"0")==0&&strcmp(b,"0")==0) 61 break; 62 l=bi_search(a); 63 f=0; 64 r=bi_search(b); 65 if(f) printf("%d\n",r-l+1); 66 else printf("%d\n",r-l); 67 } 68 return 0; 69 }
【如有错误,敬请指正,欢迎交流】