[关闭]
@morehigh 2017-02-06T02:53:13.000000Z 字数 4434 阅读 1056

CQUPT 集训队专题训练(二分/三分)

(二分/三分)


A - Strange fuction
题意:

  1. F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)
  2. x取(0100)时,F(x)的最小值。

解题思路:

  1. 先将F(x)进行求导,然后求导之后的函数值为0时,此时用二分查找方法求出x,F(x)取值最小。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. using namespace std;
  6. double minn(double x,double y)
  7. {
  8. double ans=6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;
  9. return ans;
  10. }
  11. double solve(double x,double y)
  12. {
  13. double ans=42*pow(x,6)+48*pow(x,5)+21*pow(x,2)+10*x-y;
  14. return ans;
  15. }
  16. int main()
  17. {
  18. int t;
  19. cin>>t;
  20. while(t--)
  21. {
  22. double m;
  23. cin>>m;
  24. double x=0;
  25. double y=100.0;
  26. double mid;
  27. while(y-x>1e-5)
  28. {
  29. mid=(x+y)/2.0;
  30. if(solve(mid,m)>0)
  31. y=mid;
  32. else
  33. x=mid;
  34. }
  35. printf("%.4lf\n",minn(mid,m));
  36. }
  37. return 0;
  38. }

B - Can you find it?
题意:

  1. 给出长度为L, N, M的序列a,b,c,(1<=L, N, M<=500),给出sx,(1<=S<=1000),使得存在ai+bj+ck==x;

题解:

  1. 先枚举出ai+bj的所有值,(L*M),然后对于输出的每一个x,访问c的过程二分查找a+b的和,判断是否等于x。查找过程的复杂度为o(s*m*log(l*n))

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. long long ll[600],nn[600],mm[600],ln[300008];
  7. int main()
  8. {
  9. long long l,n,m,s,x;
  10. int cas=1;
  11. while(scanf("%lld%lld%lld",&l,&n,&m)!=EOF)
  12. {
  13. for(int i=0;i<l;i++)
  14. scanf("%lld",&ll[i]);
  15. for(int i=0;i<n;i++)
  16. scanf("%lld",&nn[i]);
  17. for(int i=0;i<m;i++)
  18. scanf("%lld",&mm[i]);
  19. scanf("%lld",&s);
  20. for(int i=0;i<l;i++)
  21. {
  22. for(int j=0;j<n;j++)
  23. ln[i*n+j]=ll[i]+nn[j];
  24. }
  25. sort(ln,ln+l*n);
  26. sort(mm,mm+m);
  27. cout<<"Case "<<cas++<<":"<<endl;
  28. for(int k=0;k<s;k++)
  29. {
  30. scanf("%lld",&x);
  31. if((ln[0]+mm[0])>x||(ln[l*n-1]+mm[m-1])<x)
  32. cout<<"NO"<<endl;
  33. else
  34. {
  35. long long r=l*n-1,le=0;
  36. int flag=0;
  37. for(int i=0;i<m;i++)
  38. {
  39. while(le<=r)
  40. {
  41. long long mid=(le+r)/2;
  42. if(mm[i]+ln[mid]==x)
  43. {
  44. flag=1;
  45. break;
  46. }else if(mm[i]+ln[mid]>x)
  47. {
  48. r=mid-1;
  49. }else
  50. {
  51. le=mid+1;
  52. }
  53. }
  54. le=0;
  55. r=l*n-1;
  56. if(flag)break;
  57. }
  58. if(flag)
  59. cout<<"YES"<<endl;
  60. else
  61. cout<<"NO"<<endl;
  62. }
  63. }
  64. }
  65. return 0;
  66. }

C - Monthly Expense
最大值最小值问题
题意:

  1. N (1 N 100,000)划分为M (1 M N) 段,每一段为这一段天数花费的总和,问预算最多的那一段最少为多少?

解题思路:

  1. 利用二分的方法将假设预算,如果pi<预算,就将pi加到这一段,否则就加到下一段。若pi>预算或者num>M,表示不满足条件。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. using namespace std;
  6. int n,m;
  7. int a[100086];
  8. int sol(long long x)
  9. {
  10. long long sum=0;
  11. int num=1;
  12. for(int i=0;i<n;i++)
  13. {
  14. if(sum+a[i]<=x)
  15. {
  16. sum+=a[i];
  17. }else
  18. {
  19. num++;
  20. sum=a[i];
  21. }
  22. if(num>m||x<a[i])
  23. return 0;
  24. }
  25. return 1;
  26. }
  27. int main()
  28. {
  29. while(scanf("%d%d",&n,&m)!=EOF)
  30. {
  31. long long sum=0,maxn=0;
  32. for(int i=0;i<n;i++)
  33. {
  34. scanf("%d",&a[i]);
  35. sum+=a[i];
  36. if(a[i]>maxn)
  37. maxn=a[i];
  38. }
  39. long long l=maxn,r=sum;
  40. long long mid;
  41. while(l<=r)
  42. {
  43. mid=(l+r)/2;
  44. if(sol(mid))
  45. {
  46. r=mid-1;
  47. }else
  48. l=mid+1;
  49. }
  50. cout<<mid<<endl;
  51. }
  52. return 0;
  53. }

D - River Hopscotch
最小值最大值问题
题意:

  1. 长度为L(1 L 1,000,000,000)的河流之间有N(0 N 50,000)块石头,可以踩石头过河,Di为从开始到第i块石头的距离,求搬走 M(0 M N) 块石头后,两块石头最大距离的最小距离是多少

解题思路:

  1. 将所要求的值进行由二分求得,此值定在(0L),求得此值后进行判断搬走的石头mm>m时,说明mid此值太大

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. int d[60000];
  7. int n,l,m;
  8. int sol(int mid)
  9. {
  10. int mm=0,j=0;
  11. int temp=0;
  12. for(int i=1;i<=n+1;i++)
  13. {
  14. temp+=d[i]-d[i-1];
  15. if(temp<mid)
  16. mm++;
  17. else
  18. temp=0;
  19. if(mm>m)
  20. {
  21. return 1;
  22. break;
  23. }
  24. }
  25. return 0;
  26. }
  27. int main()
  28. {
  29. while(scanf("%d%d%d",&l,&n,&m)!=EOF)
  30. {
  31. for(int i=1;i<=n;i++)
  32. scanf("%d",&d[i]);
  33. d[0]=0;
  34. d[n+1]=l;
  35. sort(d,d+n+2);
  36. int lef=1,ri=l;
  37. int mid;
  38. while(lef<=ri)
  39. {
  40. mid=(lef+ri)/2;
  41. if(sol(mid))
  42. {
  43. ri=mid-1;
  44. }else
  45. lef=mid+1;
  46. }
  47. cout<<lef-1<<endl;
  48. }
  49. return 0;
  50. }

E - Communication System
比值最大值
题意:

  1. n (1 n 100)种设备,每一种设备有mi种选择,每一个设备有B带宽和价格P,从每一种设备中选择一个求所选中最小带宽比总的价格的最大值。(B/P

解题思路:

  1. 暴力枚举出所有带宽(此带宽定在最小带宽和最大带宽之间),再枚举出不同种类所有大于等于此带宽K的最小价格,有贪心可得,B/(价格P和的最小值)得出比值最大。(同样也可以用三分求凸函数的最大值)

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. using namespace std;
  6. int ba[105][105],pri[105][105];
  7. int m[105];
  8. int low,hign;
  9. int main()
  10. {
  11. int t,n;
  12. cin>>t;
  13. while(t--)
  14. {
  15. scanf("%d",&n);
  16. low=0xffff;
  17. hign=0;
  18. for(int i=1;i<=n;i++)
  19. {
  20. scanf("%d",&m[i]);
  21. for(int j=1;j<=m[i];j++)
  22. {
  23. scanf("%d%d",&ba[i][j],&pri[i][j]);
  24. if(ba[i][j]<low)
  25. low=ba[i][j];
  26. if(ba[i][j]>hign)
  27. hign=ba[i][j];
  28. }
  29. }
  30. double bili=0;
  31. for(int k=low;k<=hign;k++)
  32. {
  33. int sump=0;
  34. for(int i=1;i<=n;i++)
  35. {
  36. int minp=0xffff;
  37. for(int j=1;j<=m[i];j++)
  38. {
  39. if(ba[i][j]>=k&&pri[i][j]<minp)
  40. minp=pri[i][j];
  41. }
  42. sump+=minp;
  43. }
  44. if(k*1.0/sump>bili)
  45. bili=k*1.0/sump;
  46. }
  47. printf("%.3f\n",bili);
  48. }
  49. return 0;
  50. }

F - Light Bulb
三分
题意:

  1. 站在灯下的高度h0.011000)人,可以左右移动,在距离灯为D0.011000)的位置为墙壁,H0.011000)为灯的高度,并且H - h >= 0.01,求此人影子最长为多少

解题思路:

  1. 求凸函数的最大值,有题意可知此人在(0D)之间移动,当移动到某一位置时,影子的长度达到最大。
  2. 1影子未达到墙壁并在地面上移动,不能达到最长,
  3. 2当此人影子当好碰到墙壁时,所在的位置是(H-h)*D/H,最大值所在范围((H-h)*D/HD),
  4. 3影子映射到墙壁上,求出函数D-x+H-(H-h)*D/x为影子长度。

代码:

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