[关闭]
@xingxing 2017-03-31T15:18:45.000000Z 字数 4654 阅读 957

寒假第一练 二分和三分

二分三分专题


[题目][A]

Strange fuction
题目大意:
对于一个函数
F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)对于T(1<=T<=100)组数据,每组数据为y(0 < Y < 1e10),输出函数的最小值。

解题思路:
F(x)为一个凹函数,对于每个y要求函数的最小值。三分函数自变量x的区间。即设置4个变量,l=0,r=100,lmid=l+(r-l)/3,rmid=r-(r-l)/3.然后在左右区间精度范围内不断更新l和r.当lmid的函数值大于或等于rmid的函数值,则更新l为lmid,否则更新r为rmid.直到l+eps>=r。
时间复杂度O(log3n),空间复杂度O(1).

AC代码:

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

[题目][B]

Can you find it?
题目大意:
A序列:由L个数据组成,B序列:由N个数据组成,C序列:由M个数据组成。(1<= L,N,M <= 500)在3个序列中,分别选出一个数据,然后判断这三个数据之和是否等于给定的X。X有S个(1<= S <= 1000).

解题思路:
如果直接判断Ai+Bj+Ck==X是否成立,时间复杂度为: 500*500*500*log(500*500*500) * 1000,而改变一下,即判断Ai+Bj == X-Ck是否成立,时间复杂度为: 500*500*log(500*500) * 1000*500.所以先求a[i]+b[j]的值,排好序,然后求这个序列中是否有值等于X-c[k],即二分搜索。注意定义好数据类型,X可能很大,所以应该把X,a[maxn],b[maxn],c[maxn]定义为long long 类型。
时间复杂度O(L*M*log(L*M) *S*N),空间复杂度O(1).

AC代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. using namespace std;
  6. const int maxn = 505;
  7. const int absize = 505*505;
  8. long long int a[maxn],b[maxn],c[maxn];
  9. int len;
  10. long long ab[absize];
  11. int binarysearch(long long int x)
  12. {
  13. int l = 0,r = len-1;
  14. int mid;
  15. while(l <= r)
  16. {
  17. mid = (l+r)/2;
  18. if(ab[mid] == x) return 1;
  19. else if(ab[mid] > x)
  20. {
  21. r = mid-1;
  22. }
  23. else l = mid+1;
  24. }
  25. return 0;
  26. }
  27. int main()
  28. {
  29. // freopen("in.txt","r",stdin);
  30. int l,n,m;
  31. int i,j;
  32. int k = 0;
  33. long long int x;
  34. int s;
  35. while(scanf("%d%d%d",&l,&n,&m) != EOF)
  36. {
  37. len = 0;
  38. for(i = 0; i < l; i++)
  39. scanf("%lld",&a[i]);
  40. for(i = 0; i < n; i++)
  41. scanf("%lld",&b[i]);
  42. for(i = 0; i < m; i++)
  43. scanf("%lld",&c[i]);
  44. for(i = 0;i < l;i++)
  45. {
  46. for(j = 0;j < n;j++)
  47. {
  48. ab[len++] = a[i]+b[j];
  49. }
  50. }
  51. sort(ab,ab+len);
  52. sort(c,c+m);
  53. scanf("%d",&s);
  54. k++;
  55. printf("Case %d:\n",k);
  56. while(s--)
  57. {
  58. scanf("%lld",&x);
  59. if(x < ab[0]+c[0] || x > ab[len-1]+c[m-1])
  60. {
  61. printf("NO\n");
  62. }
  63. else
  64. {
  65. for(i = 0; i < m; i++)
  66. {
  67. if(binarysearch(x-c[i]))
  68. {
  69. printf("YES\n");
  70. break;
  71. }
  72. }
  73. if(i == m) printf("NO\n");
  74. }
  75. }
  76. }
  77. return 0;
  78. }

[题目][C]

Monthly Expense

题目大意:
给出N天(1 ≤ N ≤ 100,000)中,每天的金钱数money(1 ≤ moneyi ≤ 10,000),把这N天不改变顺序分成M份(1 ≤ M ≤ N),然后求出这M部分中money和的最大值,要求对于不同的划分,最大值要最小。

解题思路:
设C(x):M个部分中的money的最大值小于等于x,即:任一部分的money的和均小于等于x。然后求x的最小值。那么可以对x(max of money -1 < x <= sum of money)二分。初始化l = max-1,r = sum,mid = (l+r) / 2,x = mid,然后通过贪心可以,初始化第一部分为money1,然后合并下一天的money保证总和不大于x,即floor(moneyi + x)对应的j为前一部分的终点,继续往下贪心计算划分的部分数,然后比较划分数与M。如果划分数小于等于M,则更新r=mid,否则更新l=mid,重新计算mid的值,也就是x的假设值。直到l+1>=r停止更新返回r。
时间复杂度O(n*log(n*money)),空间复杂度O(1)

AC代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #define ll long long
  5. using namespace std;
  6. const ll maxn = 100000+5;
  7. ll money[maxn];
  8. ll n,m;
  9. ll cal(ll weigh)
  10. {
  11. ll cw = money[1];
  12. int i = 1;
  13. ll cnt = 0;
  14. while(i <= n)
  15. {
  16. while(i <= n && cw <= weigh)
  17. {
  18. i++;
  19. cw += money[i];
  20. }
  21. cnt++;
  22. if(i <= n)
  23. cw = money[i];
  24. }
  25. return cnt;
  26. }
  27. ll solve(ll l,ll r)
  28. {
  29. ll mid;
  30. while(l + 1 < r)
  31. {
  32. mid = (l+r)/2;
  33. if(cal(mid) <= m)
  34. {
  35. r = mid;
  36. }
  37. else
  38. {
  39. l = mid;
  40. }
  41. }
  42. return r;
  43. }
  44. int main()
  45. {
  46. // freopen("in.txt","r",stdin);
  47. ll i;
  48. ll sum = 0,mmax = 0;
  49. scanf("%lld%lld",&n,&m);
  50. for(i = 1;i <= n;i++)
  51. {
  52. scanf("%lld",&money[i]);
  53. if(mmax < money[i]) mmax = money[i];
  54. sum += money[i];
  55. }
  56. printf("%lld\n",solve(mmax-1,sum));
  57. return 0;
  58. }

[题目][D]

River Hopscotch

题目大意:
在起点和终点相距L(1 <= L <= 1e9)的一条河中,起点和终点之间有N(0 <= N <= 5000)个石头,两个石头之间的距离为Di(0 < Di < L),现在要拿走M个石头(0 <= M <= N),最大化相邻两个石头的距离的最小值。

解题思路:
设函数C(x):相邻的两个石头的最小距离大于等于x。即求x的最大值,可以选定x的范围为左闭右开。所以x的范围可以初始化为[0,l+1),然后对x[l,r)的范围进行二分,直到l+1 >= r,mid=(r+l)/2,每次二分后将mid代入贪心,从起点开始,取距离最大为mid,然后留下该石头,然后继续贪心,尽量留下最多的石头,然后比较留下的石头数和n-m的大小,如果留下的大于等于n-m,那么更新l=mid,否则更新r=mid。
时间复杂度O((n+2)*lo(n+2)(l+2)),空间复杂度O(1)。

AC代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. #define ll long long
  6. using namespace std;
  7. const int maxn = 50000 + 10;
  8. ll d[maxn],dlen;
  9. ll num;
  10. ll l,n,m;
  11. ll cal(ll x)
  12. {
  13. ll i,j;
  14. ll cnt = 0;
  15. i = 0,j = 1;
  16. while(j < dlen)
  17. {
  18. while(i < dlen-1 && j < dlen && d[j]-d[i] < x)
  19. {
  20. j++;
  21. }
  22. if(i < dlen-1 && j < dlen)
  23. {
  24. i = j;
  25. j = i+1;
  26. }
  27. cnt++;
  28. }
  29. return cnt-1;
  30. }
  31. ll solve(ll l,ll r)
  32. {
  33. ll mid;
  34. while(l + 1 < r)
  35. {
  36. mid = (l + r) / 2;
  37. if(cal(mid) >= num) l = mid;
  38. else r = mid;
  39. }
  40. return l;
  41. }
  42. int main()
  43. {
  44. //freopen("in.txt","r",stdin);
  45. ll i;
  46. scanf("%lld%lld%lld",&l,&n,&m);
  47. num = n-m;
  48. d[0] = 0;
  49. for(i = 1;i <= n;i++)
  50. scanf("%lld",&d[i]);
  51. d[i] = l;
  52. dlen = i+1;
  53. sort(d,d+dlen);
  54. printf("%lld\n",solve(0,l+1));
  55. return 0;
  56. }

[题目][F]

Light Bulb

题目大意:

T(T <= 100)组测试样例。
每行有实数H(灯的高度),h(人的高度),D(灯与墙之间的距离),10^-2 <= H,h,D <= 10^3,H-h >= 10^-2.
输出这个人影子的最大长度,保留三位小数。

解题思路:

H,h,D和影子长度l满足 l=D-x+H-D*(H-h)/x;x是人到墙的距离。由公式性质知,先增后减,故可以用三分来求最大值。当三分左值和右值小于某个精度的时候,就返回现在的距离l,并计算出影子长度。

AC代码:

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