【杂题】fish

        在某个海边小国,大多数居民都是渔民,这个国家的所有城镇都沿直线分布在海边。渔民们捕获大量的海鱼,但就象世界上大多数的渔民一样,他们并不喜欢吃鱼,所以他们决定从邻国收养一些贫困家庭的小孩,让他们来帮着吃鱼,国家规定每个城镇收养的贫困儿童数量必须相等。

       一条又长又直的公路贯穿整个海岸将所有的城镇连接了起来,所以每个城镇(除去第一个和最后一个)都直接和相邻的两个城镇相连接。一个小孩一年要吃掉一吨鱼,每个城镇捕获的鱼既可以在本地吃也可以运往其它城市吃,在运输过程中,每公里要上交一吨鱼作为过路费。

       已知每个城镇一年的捕鱼产量,并假设运输方案是最佳的,计算最多能收奍多少个贫困儿童。

 

输入

输入文件第一行包含一个整数N,其中1≤N≤100,000,表示城镇总数。

接下来的N行每行包含两个整数A和B,其中1≤A≤1,000,000,000,0≤B≤1,000,000,000,分别表示城镇的位置(坐标)和该城镇的捕鱼产量,所有城镇按其位置从小到大排序给出,注意问题一定存在正整数解。

 

输出

输出文件仅一行包含一个整数表示每个城镇最多能够收养的贫困儿童数量。

 

样例

输入

4

20 300

40 400

340 700

360 600

输出

415


解题思路

不难得知,最多能收养的孩子一定是在0和捕鱼最多村庄的捕鱼量之间的一个数。因此想到二分法求解。用一个ok函数来判断当前的收养孩子数能否被满足,若能被满足,向上半部分搜,若不能被满足,向下半部分搜。需要注意的是二分法一种是搜具体值,一种是搜位置(如本题),两种二分在写法上有微小差别。在ok函数中,从开头的一个村庄扫到倒数第二个村庄,先让他们满足条件,最后再判断一下最后一个村庄是否能满足他们。

详见代码。


#include <cstdio>
const int maxn = 1000010;
int n;//村庄数
int position[maxn];//坐标位置
int fish[maxn];//产量
int ok(int k)//判断能否满足供养K个小孩
{
    long long carry = 0;//记录货物流动情况
    for(int i = 0; i < n-1; i++)//从第一个村庄开始到倒数第二个村庄,若本村庄收货大于K,一定是要把多出来的运到下一个村庄去
    {
        long long x = fish[i] + carry;//x表示来到了第i个村庄以后手头有多少货物,之前运来的加上,之前借走的减去
        if(x<k) carry = x-k-(position[i+1]-position[i]);//如果本村庄不能供应 从下一个村庄借!
        else{//如果本村庄可以供应
            carry = x-k-(position[i+1]-position[i]);//把多出来的运往下一个村庄
            if(carry < 0) carry = 0;//如果运不到鱼就完了,那么只能浪费掉了。但也不至于产生负数影响下面的计算
        }
    }
    return (fish[n-1]+carry) >= k;//若最后一个村庄可以满足,那么说明可以养活K个孩子。反之亦然
}
int main()
{
    scanf("%d",&n);
    int low = 0 , high = -1;//二分搜索范围 显然答案应该在0到产量最大值范围之内
    for(int i = 0 ; i < n ; i ++){
        scanf("%d%d",&position[i],&fish[i]);
        if(fish[i] > high) high = fish[i];
    }
    while(low < high){//二分法搜索
        int mid = low + (high-low+1)/2;//此处如果mid = low + (high-low)/2; 会出现死循环情况,为了避免,应是其写成这种形式
                                       //注意二分法有两种搜索,一个是搜索位置(需要这么写),一个是搜索值(直接(low+high)/2就好)
        if(ok(mid)) low = mid;//判断能否满足供养K个小孩
        else high = mid-1;
    }
    printf("%d\n",low);
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值