二分总结:HDU 1551,4190;POJ 1905,3273,3122,3518;CF 371C

2024-06-08 23:48

本文主要是介绍二分总结:HDU 1551,4190;POJ 1905,3273,3122,3518;CF 371C,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

感觉:首先,先总结这两天做的二分题目。

因为根据这几个月以来做的CF还有组队赛,里面似有似无的存在着二分的影子,而二分以前还没有系统的做过,所以总是自己的弱项。再在终于狠下心来学习了。

学了两天,收获还是挺多的。

二分的用处太大了,不管是求简单的方程,还是求最优解方面都是不错的解题思想。

只要在线性,顺序或者有序的数据里就可以用二分来找最优的答案,而且时间平均都是O(log2 n)。题目中好像是HDU 4190吧,这题的限时是10000ms,而用二分做才用时1000ms,其优点可想而知。

不过就像《编程珠玑》中说的一样,虽然二分思路及其做法很爽,但是编写二分的程序总是错漏百出的。二分的第一个程序出现在1942年,但是直到1962年出出现了第一个没有bug的二分程序,其编写正确难度可想而知。

因为有一次做CF的时候有道题宝哥一看就知道是二分了,但是我后面看了好久都不知道是用的二分,所以就知道自己简单太弱了!这几天接连出现二分题目,所以不得不系统的学习一下了。

解题技巧:

(1)整形数据题目:l 为下界,r 为上界

一般的整形数据的题其循环都是 :

while ( l < r )   然后l=mid+1,high=mid 这各形式的;

或者有的题目边界要求比较强就得是  

while ( l <= r) 然后 l=mid+1,r=mid-1 这各形式;

还有道题就是CF 371C那道中的边界处理要求比较高就是:

while(  l+1 < r ) 然后 l=mid,r=mid


(2)浮点型题目: #define eps 1e-5

一般浮点型题目都会与精度打交道,所以势必与eps有关,因为如果如果精度要求0.01,那么如果你在 l=mid+eps这样做的话,这里我设eps为0.00001,那么时间复杂度就会乘以10^3了,那么既然二分是减少时间的,这样又会增加时间复杂度,那该怎么避免这个problem呢。

所以在HDU 1551这题上我就掉进了这个坑了,我把精度写在 l=mid+eps里了,然后直接TLE。  我把精度写在while里面的时候时间直接下降很多。因为每次都是平分,这就与eps没多大关系了,只要能接近最优答案就行。所以技巧如下:

while( r - l >eps)  然后 l=mid , r=mid;即可。

解题的基本思路就是:

while( l < r )
{mid=(l+r)/2; //如果是整数用移位>>1更加快if(gao(mid)<=m) l=mid+1;  //gao函数是处理二分枚举之后验证最佳答案是否符合的函数else r=mid; 
}


下面是这些题的代码及分析:

POJ 1905

题意:就是求棍弯曲后中点与原来的中点的距离值。

感觉:好久不做二分了,以前做的只是简单的二分,没有做过数学或者几何的二分,北邮二分题都不知道是用的二分……唉……可怜凄凄啊!!!前学好二分再说了……

思路:分析:http://blog.csdn.net/lyy289065406/article/details/6648562

看了队友教大一的PPT,哈哈,上次做了一个精度的题目,所以二分数学答案精度也是有很大的关系,二分如果精度做得不好,就得不到想要得到的答案。还是得好好学啊……

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define eps 1e-5  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define MM 2000005  
#define MN 1005  
#define INF 10000007  
using namespace std;  
typedef long long ll;  
int main()  
{  double l,n,c,s,r,low,high,mid;  while(cin>>l>>n>>c)  {  if(l<0&&n<0&&c<0) break;  low=0,high=0.5*l;  s=(1+n*c)*l;  while(high-low>eps)  {  mid=(low+high)/2;  r=(4*mid*mid+l*l)/(8*mid);  if(2*r*asin(l/(2*r))<s) low=mid;  else high=mid;  }  printf("%.3f\n",mid);  }  return 0;  
} 

POJ 3273

题意:就是给出n个数,然后分成m组,要求分得各组的花费之和应该尽可能地小,最后输出各组花费之和中的最大值。

感觉:二分可以求方程的解,这个我承认。不过这题确实有点神哦……这样也能二分,太神了,难怪以前做CF的时候遇到好多这样的题,然后都是用暴力搞不出来,听宝哥说都是用的二分,原来如此,越学越觉得二分用得真爽啊……

题意:二分枚举最佳的最大值,然后用最大值去枚举这n个数能分成的组数,逐渐逼近最优答案即可。

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define eps 1e-5  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define MM 100005  
#define MN 1005  
#define INF 10000007  
using namespace std;  
typedef long long ll;  
int n,m,a[MM],l,r,mid;  
int gao(int mid)  
{  int i,sum=0,group=1;  for(i=0;i<n;i++)  if(sum+a[i]<=mid) sum+=a[i];  else sum=a[i],group++;  if(group>m) return 1;  //mid偏小  return 0; //mid偏大  
}  
int main()  
{  scanf("%d%d",&n,&m);  for(int i=0;i<n;i++)  {  sca(a[i]);  r+=a[i];  l=max(l,a[i]);  }  while(l<=r)  {  mid=(l+r)/2;  if(gao(mid)) l=mid+1;  else r=mid-1;  }  pri(mid);  return 0;  
}

POJ 3122

题意:就是求最大的体积能每个人得到的都相同的,并且切出来的这个不能是由小的加出来的,必须是由大的切出来得到的最大值。

思路:做了前两道题,都是参考了别人的代码,现在终于自己能写二分了,虽然在处理精度的时候还不是很会,不过慢慢来就会了,第一次写的这个二分有点挫了,一点精度一失就不得答案了,真郁闷……就是寻找最大的半径,刚开始是找最大的半径的,不过精度损失严重,然后直接求体积再二分了。得到最大的体积,然后用所有的体积除这个二分的体积,然后在f+q附近就是二分的最优答案了

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define eps 1e-6  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define MM 100005  
#define MN 1005  
#define INF 10000007  
using namespace std;  
typedef long long ll;  
int n,m;  
double a[MM];  
int gao(double mid)  
{  int sum=0,i;  for(i=0;i<n;i++)  sum+=int(a[i]/mid);  if(sum<m) return 0;  return 1;  
}  
int main()  
{  int t;  sca(t);  while(t--)  {  scanf("%d%d",&n,&m);  m++;  double l,r,mid;  for(int i=0; i<n; i++)  {  scanf("%lf",&a[i]);  a[i]*=a[i]*PI;  if(r<a[i]) r=a[i];  }  l=0;  while(r-l>eps)  {  mid=(l+r)/2;  if(gao(mid)) l=mid;  else r=mid;  }  printf("%.4f\n",mid);  }  return 0;  
} 

POJ 3518

题意:就是求这个数两边的素数之差,如果是素数了的话就直接输出0.

思路:先打表再二分答案。

这题搞了好久才过,晕……刚开始找素数表时,数组开太大了不行,开太小也不行,然后哈哈学别人用char数组就可以了,以前还没这么用过……又学到了点有用的东东……

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define eps 1e-6  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define MM 100005  
#define MN 1005  
#define INF 10000007  
using namespace std;  
typedef long long ll;  
int prime[MM],cnt,k,low,high;  
char is[1299719];  
void is_prime()  
{  int i,j,k=1299709;  for(i=2;i<=k;i++)  if(!is[i])  {  prime[cnt++]=i;  for(j=i+i;j<=k;j+=i) is[j]=1;  }  
}  
void gao()  
{  int i,j,l=0,r=cnt,mid;  while(l<r)  {  mid=(l+r)>>1;  if(prime[mid]>k) r=mid;  else l=mid+1;  }  if(prime[l]>k) high=l,low=l-1;  else high=l+1,low=l;  
}  
int main()  
{  is_prime();  while(sca(k)&&k)  {  if(!is[k]) puts("0");  else  {  gao();  pri(prime[high]-prime[low]);  }  }  return 0;  
}  

CF 371C

C. Hamburgers
time limit per test
 1 second
memory limit per test
 256 megabytes
input
 standard input
output
 standard output

Polycarpus loves hamburgers very much. He especially adores the hamburgers he makes with his own hands. Polycarpus thinks that there are only three decent ingredients to make hamburgers from: a bread, sausage and cheese. He writes down the recipe of his favorite "Le Hamburger de Polycarpus" as a string of letters 'B' (bread), 'S' (sausage) и 'C' (cheese). The ingredients in the recipe go from bottom to top, for example, recipe "ВSCBS" represents the hamburger where the ingredients go from bottom to top as bread, sausage, cheese, bread and sausage again.

Polycarpus has nb pieces of bread, ns pieces of sausage and nc pieces of cheese in the kitchen. Besides, the shop nearby has all three ingredients, the prices are pb rubles for a piece of bread, ps for a piece of sausage and pc for a piece of cheese.

Polycarpus has r rubles and he is ready to shop on them. What maximum number of hamburgers can he cook? You can assume that Polycarpus cannot break or slice any of the pieces of bread, sausage or cheese. Besides, the shop has an unlimited number of pieces of each ingredient.

Input

The first line of the input contains a non-empty string that describes the recipe of "Le Hamburger de Polycarpus". The length of the string doesn't exceed 100, the string contains only letters 'B' (uppercase English B), 'S' (uppercase English S) and 'C' (uppercase English C).

The second line contains three integers nbnsnc (1 ≤ nb, ns, nc ≤ 100) — the number of the pieces of bread, sausage and cheese on Polycarpus' kitchen. The third line contains three integers pbpspc (1 ≤ pb, ps, pc ≤ 100) — the price of one piece of bread, sausage and cheese in the shop. Finally, the fourth line contains integer r (1 ≤ r ≤ 1012) — the number of rubles Polycarpus has.

Please, do not write the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cincout streams or the %I64dspecifier.

Output

Print the maximum number of hamburgers Polycarpus can make. If he can't make any hamburger, print 0.

Sample test(s)
input
BBBSSC
6 4 1
1 2 3
4
output
2
input
BBC
1 10 1
1 10 1
21
output
7
input
BSC
1 1 1
1 1 3
1000000000000
output
200000000001

思路:直接枚举答案,然后计算出他们的价值,如果大于给的钱上界就等于mid,否则下界等于mid,如此便可找出最佳答案……

这样的题可以二分,以前竟然不知道,唉……确实如魏神所说,做题还是太少了呀!!!以前也没有用二分做这些题,再在终于知道二分的用处有多大了,只要类似这种题都可以直接用二分暴力找出最佳答案的。

不过在上下界的判断语句上还不是很熟……前闭后开,前闭后闭的二分还不是分得很清楚,所以这题while中是 l+1<r 才得过,写成 l<r 就不行,无语,在下面 l=mid+1 了也不行,唉……二分就是这点还做不好了,多做几道练练吧!

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define eps 1e13  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define MM 100005  
#define MN 1005  
#define INF 10000007  
using namespace std;  
typedef long long ll;  
ll ss[4],z[4],zz[4];  
int main()  
{  char s[102];  ll i,p,l=0,r=eps,mid,cost;  scanf("%s",s);  for(i=0;i<strlen(s);i++)  {  if(s[i]=='B') ss[0]++;  else if(s[i]=='S') ss[1]++;  else ss[2]++;  }  cin>>z[0]>>z[1]>>z[2]>>zz[0]>>zz[1]>>zz[2]>>p;  while(l+1<r)  {  mid=(l+r)>>1;  cost=0;  for(i=0;i<3;i++)  if(mid*ss[i]>z[i]) cost+=(mid*ss[i]-z[i])*zz[i];  if(cost<=p) l=mid;  else r=mid;  }  cout<<l<<endl;  return 0;  
}  

HDU 4190

挺简单的一道二分题……直接枚举最佳人数就行了,然后二分找……

不过要注意进位,因为如果只要小数中有一位不为0,那就得进位了……就是gao函数里面滴……小数与整数比较看看小数部分有没有不为0的数,有则进位。

#include <iostream>  
#include <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <list>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  
#define PI acos(-1.0)  
#define mem(a,b) memset(a,b,sizeof(a))  
#define sca(a) scanf("%d",&a)  
#define pri(a) printf("%d\n",a)  
#define lson i<<1,l,mid  
#define rson i<<1|1,mid+1,r  
#define MM 500005  
#define MN 3005  
#define INF 10000007  
#define eps 1e13  
using namespace std;  
typedef long long ll;  
int a[MM],n,m;  
int gao(int mid)  
{  int i,sum=0;  for(i=0;i<n;i++)  {  double b=(a[i]*1.0)/(mid*1.0);  int c=a[i]/mid;  if(b>(double)c) sum+=c+1;  //注意进位即可  else sum+=c;  }  return sum;  
}  
int main()  
{  while(scanf("%d%d",&n,&m)&&n!=-1&&m!=-1)  {  int i,l=1,r=-INF,mid;  for(i=0;i<n;i++)  sca(a[i]),r=max(r,a[i]);  if(n==m) {pri(r);continue;} //尼玛,加了这句话反而加时15ms了  while(l<r)  {  mid=(l+r)>>1;  if(gao(mid)<=m) r=mid;  else l=mid+1;  }  pri(l);  }  return 0;  
}  

HDU 1551

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <list>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define sca(a) scanf("%d",&a)
#define pri(a) printf("%d\n",a)
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define MM 10005
#define MN 3005
#define INF 10000007
#define eps 1e-7
using namespace std;
typedef long long ll;
double a[MM];
int n,m;
int gao(double mid)
{int i,sum=0,b;for(i=0;i<n;i++)b=a[i]/mid,sum+=b;return sum;
}
int main()
{while(scanf("%d%d",&n,&m)&&(n||m)){int i;double l=0,r=-INF,mid;for(i=0;i<n;i++){scanf("%lf",&a[i]);if(r<a[i]) r=a[i];}while(r-l>eps) //放在这的话,就免去了10^3的复杂度了{mid=(l+r)/2;if(gao(mid)<m) r=mid;else l=mid; //刚开始是在这里加上eps的,但是这样超时了}printf("%.2f\n",l>=1.0?l:0);}return 0;
}


这篇关于二分总结:HDU 1551,4190;POJ 1905,3273,3122,3518;CF 371C的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043658

相关文章

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自