HDU 5037: Frog小青蛙过河问题(贪心)

一起看题目:
现在有一只青蛙 要过河,过河路线可以看成一个数轴,起点为0,终点为M。
河里面有N块石头,青蛙只能跳在石头上(或者在两岸),它一次能跳的最大距离是L。
小青蛙想少跳,而你想让他多跳。
小青蛙有可能跳不过去某个石头,因为离下一个石头太远了,所以给你特殊能力,你可以在河里面任意位置放石头,以此来帮助青蛙成功跨过距离太远的石头。
现在让你观察河里石头的情况,你可以预测在哪个地方青蛙跳不过去,然后你来放石头,确保青蛙一定能跳到对岸,聪明的青蛙会在你放完石头后才出发。
(你想让青蛙尽可能多跳几次,而青蛙是聪明的,它会尽可能少跳)
每组测试数据包括N,M,L
然后是N个数,代表石块在数轴上的位置。
输出小青蛙跳的次数

首先这N块石头的位置可能是乱序的,所以先给石子按照位置从小到大排序,并且在最后添加一个石子,它的位置是M,代表对岸(也就是终点)的位置。

设当前青蛙在now处,上一步在pre处

【分析情况】
(为方便分析:设青蛙现在在位置0处,假设它一次最多跳L = 3个,并且先不考虑终点M,只考虑在河中的过程)
1:青蛙可以顺利地跳下去,我们们不需要帮它。
(下图:黑色的位置有石头,先跳到2,然后青蛙想少跳,所以忽略4,跳到5)
在这里插入图片描述

2.第二种情况,青蛙在0就跳不了了,如下图:
在这里插入图片描述
(请思考,前两个石头你要怎么放,先考虑前两个,之后的等会再说)

因为想让青蛙多跳,所以尽可能地把石头放近一点,那第一块石头放在1,青蛙会跳到1。青蛙跳到1之后:now = 1 pre = 0———— ①

第二块石头呢? 是不是要放在2? ———— ②
如果你放在2,不要忘了,聪明的青蛙在你放完石头之后才出发,如果先进行①,又进行②, 那青蛙可以直接跳到2,而忽略位置1的石头。如下图:
在这里插入图片描述
所以②是不对的,②放的太近了,所以青蛙直接忽略①中放的石头了
我们应该稍微放远一点,放在位置4———— ③
为什么呢?因为如果放在2或者3,青蛙都可以忽略位置1的石头,直接跳到2 或者3, 也就是应该放在pre + L + 1处(这样青蛙就不会忽略中间放的那块石头了,它必须先跳到中间那块石头上,然后跳到pre+L+1处)
放石头的时候,不要放没用的石头,要保证青蛙不会忽略中间放的那块石头,这就是很多人说的1+L的方式放石头,先放在距离1处,再放在距离1+L处,如下图:河里一块石头都没有,应该怎么放石头—— 按照1,L的方1 3 1 3……
在这里插入图片描述
3.第三种情况,刚才都是从0开始的,现在不从0开始,假设青蛙已经跳了几步了,如下图:在2处石头,青蛙跳到了2,然后4有石头,跳到了4,之后没石头了,接下来怎么放呢?
在这里插入图片描述
是不是要按照1,L 1 L 1 L的方式呢?
如果按照1 L 1 L,那下一块放在5,然后放在8 ……但是,5如果有石头,青蛙就从2跳到5了,它会忽略中间那块4,刚刚谈到过这个情况,放石头不能让青蛙忽略中间的石头,所以放在pre + L + 1 = 2 + 3 + 1 = 6处,之后放在哪先不管,等会再说
4.再看一下 下面这个情况:
在这里插入图片描述
这个图和上一个图的区别就是:这个最后在5,上一个最后在4
现在看这种情况,下一个石头放在哪呢?
这种情况2 和 5 差了3个,青蛙最多就跳3个,所以青蛙必须从2跳到5,它不会忽略5的,所以按照1 L 1 L 放, 也就是放在6,9,10,13,14,17……,

在这里插入图片描述

看下面这句话:加石子要加在pre刚好不能跳到,而now能跳到的最近的那一个
pre最远跳pre + L,所以pre刚好跳不到的那个点是pre+L+1,
这个点就是放石子的点
5.现在考虑这种情况:pre = 0, now = 2, L = 4(一次跳4个);如图
在这里插入图片描述
下一块石头放在哪?pre + L + 1 = 0 + 4 + 1 = 5; 然后pre = 2, now = 5;
继续,下一块石头放在pre + L + 1 = 2 + 4 + 1 = 7;.……如下图:
在这里插入图片描述
如果一直重复下去,这个不是按照 1 L 1 L 1 L 放石头
而是每隔3个 2个 3个 2个 3个 2个…… 放一个石头
但是 : 3 + 2 = 1 + L = 5; 所以 只要每隔 1 + L 个位置,都要跳两次,
比如上图,0 – 5,隔了5个位置, 跳两次, 2 – 7,隔了5个位置,跳两次
可以这样解释:从pre 跳到now,1次; 从 now 跳到 pre + L + 1 ,又一次
中间隔了 pre + L + 1 – pre = 1 + L 个间隔
之后pre 和now 更新,继续重复下去
6.以上问题化简了不少东西,比如没考虑终点,现在讨论有终点的情况:

在这里插入图片描述
按照5中石头的摆放方式,假设8是终点,那么青蛙在5的时候,应该忽略7,直接跳到8。如果9是终点,也应该忽略7,直接跳到9。
最后一步怎么判断用不用忽略某个石头呢?看下图情况:
初始情况:N = 1(在2有石头),M = 20,21,22(多讨论几种情况),L = 4,
在这里插入图片描述

青蛙跳到2,now = 2,pre = 0.然后按照pre + L + 1,更新now,pre重复下去
在这里插入图片描述
中间有重复的2 3 2 3 2 3,我们可以先数一下中间经过了几个2 + 3(也就是1+L),设int cishu;
cishu = (下一块石头的位置 - now) / (1 + L);
上图情况就是cishu = (20 - 2) / 5 = 3 余3
所以int yushu;
yushu = (下一块石头位置 - now) % (1 + L);
在这里插入图片描述
这种情况就解完了
现在假设M = 19, 最后一步怎么跳?应该忽略17,从15直接跳到19.
对于这种情况: cishu = (19 - 2) / 5 = 3;
yushu = 2; 从12 到 17 是完整的一个3+ 2 (1 + L),但是余数刚好可以拼凑到2里面,也就是最后从15到17 和从17 到19,可以拼成一步,上一种情况余数是3,余数的上一步跳了2, 3和2超过了4,所以多跳一步。
所以判断最后一步用不用合并,只要看余数和余数的上一步加起来是不是能一步跳过去
现在的问题是,余数的上一步是多少?
在这里插入图片描述
终点还是19,从2 到19 经过了3个完整的 2 + 3(1 + L),余下2个,
判断余下的2个,和余数的上一步 2 能不能合并跳
这个余数的上一步是怎么来的呢?——是 now – pre = 2 – 0得到的
放石头的位置是pre刚好达不到,且now能到的最近位置,这一点决定了是按照
1 + 4 或者 2 + 3 ……的方式放石头,但是1+4还是2+3,完整的1次的长度加起来还1+L
余数的上一步由now – pre决定,体现在代码中用上次跨越的距离(distance)表示了
if(distance + yushu > L) sum += cishu * 2;
else sum += cishu * 2 + 1;

还有一种情况:如果在2,19有石头,终点是26,这种情况可以由以下两种情况组合:
把19当终点。
把19当起点,看和终点有几个1+L,是一个重复的过程
还有一种情况:
在这里插入图片描述
初始distance = 0,sum = 0;
青蛙可以跳4步,1 2 3 4 6都有石头,
它应该直接跳到4,但是我们考虑的是
cishu 和yushu 和yushu + distance是否 > L
所以让它跳到1,cishu = 0,yushu = 1, yushu + distance = 1,
所以可以合并:
{
sum += 2 * cishu = 0;
///但是distance也要更新,distance 应该合并之后跳的步数
distance += yushu;

}

还要考虑的情况是第一步的问题,第一次如果yushu > 0,一定要多跳一步,因为第一步没有前一步,所以它不能合并到前一步,
所以保证第一步如果有余数,就要让distance + yushu > L,
所以distance赋初值L
当然也可以先单独处理第一步的,懒得写了

另外:
用c++的cin cout 还是会TLE,改成scanf,printf之后终于对了


#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 200005;
int rock[maxn];
int main()
{
    int t,n,m,l,casee = 1;
    cin >> t;
    while(t--)
    {
        cin >> n >> m >> l;
        for(int i = 1; i <= n; ++i)
            scanf("%d",&rock[i]);   ///rock[0] = 0把起点也当做石头并且位置为0
        sort(rock,rock+n+1);
        rock[1+n] = m; ///把对岸也当做石头并且位置为m

        int sum = 0,distance = l,cishu,yushu;
        for(int i = 0; i <= n; ++i)
        {
            cishu = (rock[i+1] - rock[i]) / (1 + l);
            yushu = (rock[i+1] - rock[i]) % (1 + l);
            if(yushu + distance > l)
            {
                sum += 2 * cishu + 1;
                distance = yushu;
            }
            else
            {
                sum += 2 * cishu;
                distance += yushu;
            }
        }
        printf("Case #%d: %d\n",casee++,sum);
    }


    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值