加强赛第一轮题解

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
",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;
}

如要转载请注明转载出处:http://www.cnblogs.com/WArobot
原文地址:https://www.cnblogs.com/WArobot/p/6523584.html