[关闭]
@zhshh 2018-07-21T15:43:33.000000Z 字数 2506 阅读 1061

二分和三分题

@zhshh cpp OI 二分


二分

愤怒的牛(其他:跳石头,丢瓶盖)

回到顶部

算法

二分答案区间,每次check,检查两个标记的距离,如果小于x,那么去掉。判断去掉的个数。

题目

Farmer John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,...,xN (0<=xi<=1,000,000,000)。
他的C(2<=C<=N)头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?
输入格式:
第1行:两个用空格隔开的数字N和C。
第2~N+1行:每行一个整数,表示每个隔间的坐标。
输出格式:
输出只有一行,即相邻两头牛最大的最近距离。

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. int n,m;
  5. int a[10000010];
  6. int check(int x){
  7. int used=0;
  8. int last=-0x3f3f3f3f;
  9. for(int i=1;i<=n;i++){
  10. if(a[i]-last>=x){
  11. used++;
  12. last=a[i];
  13. }
  14. }
  15. if(used>=m)return true;
  16. return false;
  17. }
  18. int search(){
  19. int left=1;
  20. int right=a[n];
  21. int mid;
  22. int ans;
  23. while(left<=right){
  24. mid=(left+right)/2;
  25. if(check(mid)){
  26. left=mid+1;
  27. ans=mid;
  28. }else{
  29. right=mid-1;
  30. }
  31. }
  32. return ans;
  33. }
  34. void init(){
  35. cin>>n>>m;
  36. for(int i=1;i<=n;i++){
  37. cin>>a[i];
  38. }
  39. sort(a+1,a+n+1);
  40. }
  41. void work(){
  42. cout<<search();
  43. }
  44. int main(){
  45. init();
  46. work();
  47. return 0;
  48. }

三分

曲线求极值

回到顶部

题目

【题目描述】
明明做作业的时候遇到了n个二次函数Si(x)= ax2 + bx + c,他突发奇想设计了一个新的函数F(x) = max(Si(x)), i = 1...n.
明明现在想求这个函数在[0,1000]的最小值,要求精确到小数点后四位四舍五入。
【输入数据】
输入包含T 组数据 (T < 10) ,每组第一行一个整数 n(n ≤ 10000) ,之后n行,每行3个整数a (0 ≤ a ≤ 100), b (|b| ≤ 5000), c (|c| ≤ 5000) ,用来表示每个二次函数的3个系数,注意二次函数有可能退化成一次。
【输出数据】
每组数据一个输出,表示新函数F(x)的在区间[0,1000]上的最小值。精确到小数点后四位,四舍五入。

算法

易得很多二次函数凑到一块,然后取max,可以从轮廓上看出来一定是V型,只是有折点什么的,两边斜率不太一样。因此整体就构造一个,然后正常三分即可。

  1. // 代码丢了。。。

连续区间最大和

回到顶部

1.不要求长度

从左往右累加,然后判断。
,如果那么的加入会比直接从开始的值更大,那么加上。如果那么直接从开始有更优解。此时

2.长度至少为L最大序列

二分结果,判定是否有一个长度不小于L的子序列,平均数不小于二分check()的值
对于这个check()条件(注意,这里并不需要确保求出最大值,只是求一个合适的满足的解即可,求值是二分的事,这个不能弄混
这样如果这个子序列每个数都减去二分的值,那么就转化为了求最大连续子序列的长度能不能到长度为L,这个可以像上面那样说的用尺取法滚动取值。因为长度为L,那么[l,r]的r一定不小于L,所以r从L开始计算,利用前缀和快速计算长度。

  1. #include <cmath>
  2. #include <cstdio>
  3. #define eps 1e-6
  4. #include <iostream>
  5. #define MX 100010
  6. using namespace std;
  7. double a[MX],b[MX],c[MX];
  8. int main(){
  9. int n,length;
  10. cin>>n>>length;
  11. for(int i=1;i<=n;i++){
  12. // cin>>a[i];
  13. scanf("%lf",&a[i]);
  14. }
  15. double left=-1e+6;
  16. double right=+1e+6;
  17. double mid;
  18. while(left<=right && fabs(left-right)>eps){
  19. mid=(left+right)/2;
  20. //---------------check(mid)--------------------
  21. for(int i=1;i<=n;i++){//减去check的mid
  22. b[i]=a[i]-mid;
  23. }
  24. for(int i=1;i<=n;i++){//计算前缀和
  25. c[i]=c[i-1]+b[i];
  26. }
  27. double mval=1e+8;//充分大的cnt-l以及之前的最小值
  28. double ans=-1e+8;//充分小的ans,即记录符合条件长度的
  29. //一段区间,最大长度是多少
  30. for(int i=length;i<=n;i++){//下面确保了这段长度大于等于L
  31. mval=min(mval,c[i-length]);//val始终为i-l前面的最小值
  32. ans=max(ans,c[i]-mval);//长度大于等于L的区间最大和是?
  33. }
  34. //-----------------------------------------
  35. if(ans>0){//b[i]和大于零==>a[i]和大于avr*(r-l+1),即sum
  36. left=mid;
  37. }else{
  38. right=mid;
  39. }
  40. }
  41. cout<<int(right*1000)<<endl;
  42. return 0;
  43. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注