[关闭]
@wwwqeqeqeqe 2017-03-26T15:57:12.000000Z 字数 4846 阅读 680

寒假训练专题一(二分、三分)


A

题目大意

题目先给出了一个算式,让你输入一个0~1e10的数y,找到在x范围为0~100内f(x)的最小值。

解题思路

因为当一个数达到级值的时候,f(x)'=0,而从原式可知,f(x)'是一个单调递增的函数,所以f(x)'<0时,x<x0,f(x)'>0时,x>x0,f(x)在x0处取得最小值。因此,可以用二分的方式查找,看他们的左右边界的中间值处f(x)'的大小,如果大于0,则这个中间值成为右边界,否则成为左边界,一直找到左边界与右边界的差值小于1e-5为止,此时得到的中间值即为x0。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<cmath>
  5. #include<algorithm>
  6. using namespace std;
  7. const double eps=1e-5;
  8. int T;
  9. double x,y,ans;
  10. double ff(double a,double y)
  11. {
  12. return 42*pow(a,6)+48*pow(a,5)+21*pow(a,2)+10*a-y;
  13. }
  14. double f(double mid,double y)
  15. {
  16. return 6*pow(mid,7)+8*pow(mid,6)+7*pow(mid,3)+5*pow(mid,2)-y*mid;
  17. }
  18. int main()
  19. {
  20. scanf("%d",&T);
  21. while(T--)
  22. {
  23. scanf("%lf",&y);
  24. double l=0,r=100,mid;
  25. while(l+eps<=r)
  26. {
  27. mid=(l+r)/2;
  28. if(ff(mid,y)>0)
  29. r=mid;
  30. else
  31. l=mid;
  32. }
  33. printf("%.4lf\n",f(mid,y));
  34. }
  35. return 0;
  36. }

B

题目大意

给你三个数组A、B、C,从这三个数组中,每个数组选出一个数字,问是否存在这样的三个数,使他们的和为x。

解题思路

题目其实就是问的能否找到三个数a,b,c,使a+b+c=x这个等式成立。如果用暴力三重循环,会超时。所以可以把这个等式改为a+b=x-c,先将a+b组成一个数组并排序,再用一个循环循环C这个数组里面的数得到x-c,再利用二分,二分a+b这个数组的坐标,如果得到的ab[mid]大于x-c,则右边界r=mid-1,如果小于,即左边界l=mid+1,否则就找到三个数使得a+b=x-c,返回1,找不到就返回0,最后根据返回的数输出YES 或者 NO。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int maxn=1e3+5;
  8. int a[maxn],b[maxn],c[maxn],ab[maxn*maxn];
  9. int num,L,M,N,S,x;
  10. int fnd(int xx)
  11. {
  12. int l=0,r=num-1;
  13. while(l<=r)
  14. {
  15. int mid=l+r>>1;
  16. if(ab[mid]>xx)
  17. r=mid-1;
  18. else if(ab[mid]<xx)
  19. l=mid+1;
  20. else
  21. return 1;
  22. }
  23. return 0;
  24. }
  25. int main()
  26. {
  27. int casee=0;
  28. while(~scanf("%d%d%d",&L,&N,&M))
  29. {
  30. num=0;
  31. memset(a,0,sizeof(a));
  32. memset(b,0,sizeof(b));
  33. memset(c,0,sizeof(c));
  34. for(int i=0; i<L; i++)
  35. scanf("%d",&a[i]);
  36. for(int i=0; i<N; i++)
  37. scanf("%d",&b[i]);
  38. for(int i=0; i<M; i++)
  39. scanf("%d",&c[i]);
  40. for(int i=0; i<L; i++)
  41. for(int j=0; j<N; j++)
  42. ab[num++]=a[i]+b[j];
  43. sort(ab,ab+num);
  44. scanf("%d",&S);
  45. printf("Case %d:\n",++casee);
  46. while(S--)
  47. {
  48. scanf("%d",&x);
  49. int tt=0;
  50. for(int i=0; i<M; i++)
  51. {
  52. int xx=x-c[i];
  53. if(fnd(xx))
  54. {
  55. tt=1;
  56. printf("YES\n");
  57. break;
  58. }
  59. }
  60. if(!tt)
  61. printf("NO\n");
  62. }
  63. }
  64. return 0;
  65. }

C

题目大意

给你两个数m和n,n表示一共会给你n天的预算,m表示要将这n天分成m份,然后给出这n天的预算,让你求出这m份中最小的最大值。

解题思路

要将n个数分为m份,求到最小的最大值,那么这个数最小就是这n个数的最大值,最大就是这n个数的和。因此,可以用二分的方式,令l为这n个数中的最大值,r为sum。每次将l和r的中间值带入进行计算,一共可以分成多少份,如果分的份数小于等于m,则表示我们需要的答案比这个mid小,则令r=mid-1,反之则表示答案比我们在mid下得到的份数大,则l=mid+1.直到l>r为止。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int maxn=1e5+5;
  8. long long a[maxn];
  9. long long n,m;
  10. long long Cnt(long long mm)
  11. {
  12. long long sum=0,cnt=1;
  13. for(int i=0;i<n;i++)
  14. {
  15. sum+=a[i];
  16. if(sum>mm)
  17. {
  18. sum=a[i];
  19. cnt++;
  20. }
  21. }
  22. return cnt;
  23. }
  24. int main()
  25. {
  26. while(~scanf("%lld%lld",&n,&m))
  27. {
  28. memset(a,0,sizeof(a));
  29. long long sum=0,mx=-maxn-8;
  30. for(int i=0;i<n;i++)
  31. {
  32. scanf("%lld",&a[i]);
  33. mx=max(mx,a[i]);
  34. sum+=a[i];
  35. }
  36. long long l=mx,r=sum,mid;
  37. while(l<=r)
  38. {
  39. mid=l+r>>1;
  40. long long k=Cnt(mid);
  41. if(k<=m)
  42. r=mid-1;
  43. else
  44. l=mid+1;
  45. }
  46. printf("%lld\n",mid);
  47. }
  48. return 0;
  49. }

D

题目大意

给你三个数L,N,M。L表示起点到终点的距离,N表示一共有的石头的数量,M表示要去除的石头的数量,求去除这些石头之后,石头与石头之间以及石头与河岸之间最大的最小距离。

解题思路

先将两个河岸的距离标记,左端的河岸标记为l=0,右端的河岸标记为r=L。之后每次枚举一个距离mid=(l+r)/2,然后算出在该种距离下一共可以移除多少个石头,如果可移除的石头大于M,则表示这个距离太大,就令r=mid-1,否则就表示这个距离太小,则l=mid+1,直到l>r为止,因为是取得最大的最小距离,那么最终得到的l即为最大的最小距离。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int maxn=5e4+5;
  8. int L,M,N;
  9. int n[maxn];
  10. int fnd(int l,int r,int k)
  11. {
  12. int mark,cnt,mid;
  13. while(l<=r)
  14. {
  15. mark=0;
  16. cnt=0;
  17. mid=l+r>>1;
  18. // cout <<l<< " " << r << " " <<mid ;
  19. for(int i=1;i<=N+1;i++)
  20. {
  21. if(mid>=n[i]-n[mark])
  22. cnt++;
  23. else
  24. mark=i;
  25. }
  26. if(cnt>k)
  27. r=mid-1;
  28. else
  29. l=mid+1;
  30. // cout << " " << cnt << endl;
  31. }
  32. return l;
  33. }
  34. int main()
  35. {
  36. cin>> L >> N >> M;
  37. for(int i=1;i<=N;i++)
  38. cin >> n[i];
  39. n[0]=0;
  40. n[N+1]=L;
  41. sort(n+1,n+N+1);
  42. printf("%d\n",fnd(0,L,M));
  43. return 0;
  44. }

E

题目大意

某公司要建立一套通信系统,可以由n个厂家提供,每个厂家一共有m种设备,而每种设备有他们自己的带宽b和价格p,要求你从这n个厂家每个厂家挑选一种设备,使得得到的b/p值最大,其中这个b取所有设备中的最小值,p为所有设备的价格总和。

解题思路

可以从题目中得到一个递推公式dp[i][j]=min(dp[i][j],dp[i-1][k]+p),其中i为取到了第i家公司,j为取到这家公司的这个设备时,最小的带宽b,dp[i][j]的值为取到这个设备时的总价格p。最后取完所有的公司,再将所有的带宽跑一遍,取最大的b/p值。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std ;
  7. const int INF=0x3f3f3f3f;
  8. int t,n,m,b,p;
  9. int dp[505][2005];
  10. int main()
  11. {
  12. cin >> t;
  13. while(t--)
  14. {
  15. cin >> n;
  16. for(int i=0;i<=n;i++)
  17. for(int j=0;j<2005;j++)
  18. dp[i][j]=INF;
  19. for(int i=1;i<=n;i++)
  20. {
  21. cin >> m;
  22. for(int j=1;j<=m;j++)
  23. {
  24. cin >> b >> p;
  25. if(i==1)
  26. dp[i][b]=p;
  27. else
  28. {
  29. for(int k=0;k<1005;k++)
  30. {
  31. if(dp[i-1][k]!=INF)
  32. {
  33. if(b>k)
  34. dp[i][k]=min(dp[i][k],dp[i-1][k]+p);
  35. else
  36. dp[i][b]=min(dp[i][b],dp[i-1][k]+p);
  37. }
  38. }
  39. }
  40. }
  41. }
  42. double ans=0,num;
  43. for(int i=0;i<2005;i++)
  44. {
  45. num=(double)i/dp[n][i];
  46. ans=max(ans,num);
  47. }
  48. printf("%.3lf\n",ans);
  49. }
  50. return 0;
  51. }

F
题目大意

一个人在自己的家中,其中灯的高度为H,人的高度为h,房间总宽度为D,求人在他的家中的影子最长能有多长。

解题思路

从题目的图中可知,要求的影子的长为人在地板上的投影和在墙上的投影之和。而且可以知道如果人在灯下的话影子的长度为0,靠墙站时影子长度为h,而且明显可知影子最长的时候在这两个状态中间。因此,影子的长度是一个先增后减的过程。而从图中可以得到,如果我们设人在墙上的投影长为x,那么可以得到影子总的长度为(h-x)/(H-x)*D+x,(PS:不知道怎么来的看看高中数学。) 然后根据三分原理,可以算出答案。

AC代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const double esp=1e-4;
  8. double H,h,D;
  9. int T;
  10. double f(double x)
  11. {
  12. return (h-x)/(H-x)*D+x;
  13. }
  14. int main()
  15. {
  16. double mid,mmid,l,r;
  17. cin >> T;
  18. while(T--)
  19. {
  20. cin >> H >> h >> D;
  21. l=0;
  22. r=h;
  23. while(r-l>esp)
  24. {
  25. mid=(l+r)/2;
  26. mmid=(mid+r)/2;
  27. if(f(mid)>f(mmid))
  28. {
  29. r=mmid;
  30. }
  31. else
  32. l=mid;
  33. }
  34. printf("%.3lf\n",f(l));
  35. }
  36. return 0;
  37. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注