加强赛第一轮题解
2017/3/7###
类型:二分查找 二分搜索
A
POJ1064
题意:给出n条线段,以米的单位给出,小数点后两位(精确到厘米),要你对这些线段裁剪,裁剪出m条等长的线段,并且让这些线段尽可能长另外线段的长度不能小于1厘米,如果筹不够m条,输出0.00。
思路:二分搜索0~1e9之间的值,每次找到一个mid,判断mid是否符合题意。对于1e9的数据二分30次就足够找到答案,循环100次能够提高精度。
新知识:double floor( double arg ); 在math头文件里,作用是“向下取整”,即取不大与arg的最大整数
#include "iostream"
#include "cstdio"
#include "algorithm"
#include "cmath"
using namespace std;
int n,k;
double s[20000];
// 传入长度m,判断 按m切割出现的段数sum余最少段数k的关系
// 1.sum<=k 说明切割长度长
// 2.sum>k 说明切割合适,但仍可能有更加优秀的方案
bool judge(double m){
int sum = 0;
for(int i=0;i<n;i++){
sum += (int)(s[i]/m);
}
return sum<k;
}
int main(){
while(cin>>n>>k){
for(int i=0;i<n;i++) cin>>s[i] ;
// 采用二分搜索
double l=0 , r=0x3f3f3f3f , mid;
for(int i=0;i<100;i++){
mid = (l+r)/2;
if(judge(mid)) r=mid;
else l=mid;
}
printf("%.2lf\n",floor(r*100)*0.01);
}
return 0;
}
B
POJ2456
题意:农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
思路:此问题是寻找一个最大的最小距离,使得C头牛任意两头之间的最短距离最大值。两头牛之间的距离最短为0,最长为两端牛的距离,因此只需要二分0~Max_Len,寻找中间值mid。对于中间值mid我们需要检查符合 temp+mid<=x[i] 的次数,因为mid相当于当前任意两头牛之间的最短距离最大值,如果之前已经分配牛的位置加上现在最短距离最大值还要比X[i]小的话,那么X[i]位置处便可以放置一头牛(cnt++),最后比较按照mid为最短距离最大值放置牛的个数cnt和C的大小关系,如果cnt<C说明mid值取大了,r=mid,反之l=mid。
#include "iostream"
#include "cstdio"
#include "algorithm"
using namespace std;
const int M = 1e6;
int n,c;
int s[M];
// 1.cnt<c 说明二分的距离m大了
// 2.cnt>=c 说明二分的距离m小了
bool check(int mid){
int cnt = 1;
// tmp是之前的放置牛位置
int tmp = s[0];
for(int i=1;i<n;i++){
if(tmp+mid<=s[i]) cnt++ , tmp=s[i];
}
// 下面代码有错误,为什么会出错呢?
// 仔细分析这两段代码是能够发现其中的区别的!
//
// for(int i=1;i<n;i++){
// if(s[i]-s[i-1]>=m) cnt++;
// }
return cnt<c;
}
int main(){
while(scanf("%d%d",&n,&c )!=EOF){
for(int i=0;i<n;i++) cin>>s[i];
sort(s,s+n);
int l = 0 , r = (s[n-1]-s[0]+1) , mid;
// 二分最大距离得到一个合适的最小距离
while(r-l>1){
mid = (l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
cout<<l<<endl;
}
return 0;
}
C
POJ3258
题意: 牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离,现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值。
思路:这个题和POJ2456相似,从N个石头中取出M个石头使得剩下石头之间的最小距离的值最大。也就是说需要在N块石头中找到N+1-M块石头,让它们之间的最小距离最大。
我们用类比的方法就能很好的理解这道题,这道题中的N块石头相当于B题中的N个隔间,去掉M块石头后剩余N+1-M个石头(加上河岸两端)就相当于B题中的C头牛。B题是在N个隔间里尽量相隔远来放置C头牛,此题是在N块石头间尽量相隔较远来放置N+1-M块石头。
两端河岸相当于两块石头,也就是D[0]=0 , D[N+1]=L。首先用二分枚举石头之间的最小距离最大值mid,每次都检测 temp+mid<=D[i],temp是之前选定的石头,就相当于从temp跳跃过来,如果之前石头的位置 + 现在枚举的最短距离最大值 <= 下个石头位置,也就是说现在的mid的确是比D[i]到temp的距离要小的,记录下来相邻距离小于等于mid的块数cnt 和 N+1-M比较,如果cnt<N+1-M说明mid值取大了,反正mid取小了
#include "iostream"
#include "algorithm"
#include "cstdio"
using namespace std;
int L,N,M;
int D[50000+10];
bool check(int mid){
int cnt = 0; // cnt记录传入的mid能够去掉石头数
int tmp = 0; // s[0] = 0;
for(int i=1;i<=N+1;i++){
// 最小距离+之前点到岸边的距离<=D[i]
if(mid+tmp<=D[i]) cnt++,tmp=D[i];
}
return cnt >= N+1-M;
}
int main(){
while (cin>>L>>N>>M) {
for(int i=1;i<=N;i++) cin>>D[i];
D[0] = 0 , D[N+1] = L;
// 排一下石头到河边的距离D
sort(D+1,D+N+1);
int l = 0 , r = 1e9;
while(r-l>1){
int mid = (l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
cout<<l<<endl;
}
return 0;
}