poj 3258

这道题的解法比较巧妙(一开始没有想到)

用了二分搜索来确定解

一开始我的想法是找到一个距离最小的间隔,如果在边上(间隔起点为0 Or 间隔终点为N+1),那么就和唯一相邻的合并,否则的话向两边找,如果左边的比右边的长,

就和右边的合并(提高最小间隔的数值),如果右边的长,相同道理和左边合并;两边一样长时,继续向两侧找;如果一直试到边上两边都一样长,那么随便选一个;

如果在一侧碰到边以前两边都一样长,那就向长的那一边合并

但是这在有多个最小值时无法确定该破坏哪个间隔,问题会越来越复杂


看了网上二分搜索+greedy的例子,觉得这种方法解题的角度和我之前的想法是完全不同的!

参考的解法是这样做的:

首先,二分间隔长度,计算出使得最小间隔<当前间隔长度时,去掉的最少石头是多少;

假设有两个间隔d1,d2, d1 > d2,对应的石头数分别为n1,n2,那么n1 <= n2(因为去掉n2个已经能满足最小间隔<d2,那么自然最小间隔<d1)

反过来考虑,给定可以去掉的石头总数时,这种方法可以得到的最小间隔是最大的;


那么怎样确定这个需要去掉的最小的石头数呢?

这里用到了greedy algorithm,即从第一个间隔开始加,在每个当前长度内放尽可能多的间隔

证明:

对于当前的给定间隔长度,假设存在最优解(去掉的石头最少)使得a11,...,a1i1个间隔在第一个段内, ak1...ak ik在第k个间隔内,

且a21可以放入第一个段内使得第一段的长度不超过当前长度,那么把它从第二段拿到第一段,所得到的仍然是最优解,(因为第二个长度变短了,而已知第一个不超过当前长度),可以得到另一组最优解;

这也是greedy的一种常用证明方法



代码如下:

#include<iostream>
#include<string.h>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn = 50050;


int l,m,n;
int dist[maxn];

int binarySearch(int DeleteNum,int,int maxlen);
int main(){
    int i,j;
    while(scanf("%d%d%d",&l,&n,&m)!=EOF){
        memset(dist,0,sizeof(dist));
        dist[0] = 0;
        dist[n+1] = l;
        for(i = 1; i <= n; ++i){
            scanf("%d",dist+i);
        }
        int rocknum = n + 2;
        sort(dist,dist + rocknum);
        int lowerbound = l;
        for(i = 1; i < rocknum; ++i){
            lowerbound = min(lowerbound,dist[i] - dist[i-1]);
        }
        //printf("%d\n",lowerbound);
        printf("%d\n",binarySearch(m,lowerbound,l));
    }
    return 0;
}

int binarySearch(int todelete,int lowerbound,int totaldist){
   // printf("to delete: %d\n",todelete);
    int minlen = lowerbound, maxlen = totaldist,midlen,i,j,rocknum = n + 2;
    while(minlen <= maxlen){
        midlen = (minlen + maxlen) >> 1;
        int cntdelete = 0;
        //globalvariable l为总长度,m为deletestone数,n为中间石头数
        int s = 0, e = 1,rocknum = n + 2;
        while(e < rocknum){
            if(dist[e] - dist[s] >= midlen){
                s = e; ++e;
            }
            else{
                ++e; ++cntdelete;
            }
        }
        if(cntdelete > todelete){
            maxlen = midlen - 1;
        }
        else{
            minlen = midlen + 1;
        }
    }
    return maxlen;
}

通过这道题认真复习了一下二分法~对于二分的取等条件和正确性也有了更深入的思考~

btw:开始二分部分代码写错了,应该是根据结果修改上下界而非midlen,因为这个值每次循环都要重新计算~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值