POJ - 3258 River Hopscotch 二分

题目大意:给出河的宽度L和N块石头,现在要求移除M块石头,使得石头间的距离的最小值达到最大(起点和终点都有一块石头,但这两块石头不能移除)

解题思路:最小值的最大值,肯定用二分了 
如果存在最优的距离,那么移走的石头数量肯定刚好是M块的 
枚举的时候判断移除石头的数量,只需要从起点开始枚举,然后计算一下在最小跳跃距离内的石头有几个,有几个就移除几个,最后判断移除了多少个石头 
如果移走的数量大于M,就表示所枚举的长度偏大 
如果移走的数量小于M,就表示所枚举的长度偏小 
有一种比较特殊的情况,就是N = M的情况,这种情况下枚举的很多长度都是符合的,但是答案只有一个,所以移除数量等于M的时候不代表已经找到解了,应该再长度加1判断一下

#include<stdio.h>
 
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
 
   int L;  //河总长
    int n;  //河中石头数(除起点S和终点外E)
    int m;  //移除石头数
 
    while(cin>>L>>n>>m)
    {
        /*Input & Initial*/
 
        int* dist=new int[n+2];  //第i块石头到起点石头的距离为dist[i]
        dist[0]=0;    //起点S
        dist[n+1]=L;  //终点E
 
        int low=0;   //上界(一次跳跃的最短距离)
        int high=L;   //下界(一次跳跃的最大距离)
        for(int i=1;i<=n;i++)
        {
                cin>>dist[i];
        }
        sort(dist,dist+(n+2));
 
 
           //根据石头到S的距离升序排列 
 
    while(low<=high)
        {
            int mid=(low+high)/2;  //对最大跳和最小跳的距离折中,二分查找mid相对于最优解是偏大还是偏小
                                   //假设mid是移除m个石头后的最短距离
 
            int delrock=0;    //利用当前的mid值能移除的石头数
            int sum=0;   // 这里是 连续距离的累加值
                         //当在第i个距离累加后sum
 
            for(int i=1;i<=n+1;)
            {
                if( (sum+=dist[i]-dist[i-1]) < mid)//如果值<mid,则需要去掉一个石头
                {
                    i++;
                    delrock++;
                }
                else   //当从第i个距离累加到i+k个距离后,若sum>mid,则k个距离作为一段
                {
                    i++;
                    sum=0;  //sum置0,从第i+k+1个距离重新累加
                }
            }
 
    		if(delrock<=m)   //本题难点之一:即使delrock==m也不一定找到了最优解
//有一种比较特殊的情况,就是delrock = M的情况,这种情况下枚举的很多长度都是符合的,但是答案只有一个,所以移除数量等于M的时候不代表已经找到解了,应该再长度加1判断一下
                low=mid+1;   //用当前mid值移除的石头数小于规定数,说明mid偏小
            else            
                high=mid-1;  //反之mid偏大
		}
 		printf("%d\n",low-1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Harington/article/details/82011560