ACWing 730. 机器人跳跃问题(二分,贪心)

AcWing 730. 机器人跳跃问题

原题链接:AcWing 730. 机器人跳跃问题

题目描述

机器人正在玩一个古老的基于DOS的游戏。

游戏中有N+1座建筑——从0到N编号,从左到右排列。

编号为0的建筑高度为0个单位,编号为 i 的建筑高度为H(i)个单位。

起初,机器人在编号为0的建筑处。

每一步,它跳到下一个(右边)建筑。

假设机器人在第k个建筑,且它现在的能量值是E,下一步它将跳到第k+1个建筑。

如果H(k+1)>E,那么机器人就失去H(k+1)-E的能量值,否则它将得到E-H(k+1)的能量值。

游戏目标是到达第N个建筑,在这个过程中能量值不能为负数个单位。

现在的问题是机器人至少以多少能量值开始游戏,才可以保证成功完成游戏?

输入格式
第一行输入整数N。

第二行是N个空格分隔的整数,H(1),H(2),…,H(N)代表建筑物的高度。

输出格式
输出一个整数,表示所需的最少单位的初始能量值上取整后的结果。

数据范围

1≤N,H(i)≤105

输入样例1:

5
3 4 3 2 4

输出样例1:

4

输入样例2:

3
4 4 4

输出样例2:

4

输入样例3:

3
1 6 4

输出样例3:

3

贪心

若H[k+1] > E[k],则E[k+1] = E[k] - (H[k+1]-E[k]) = 2*E[k]-H[k+1]

若H[k+1] <= E[k],则E[k+1]= E[k] + (E[k]-H[k+1]) = 2*E[k]-H[k+1]

故无论E[k]与H[k+1]的大小关系,E[k+1] = 2*E[k]-H[k+1]恒成立

而为了使开始时能量最少,应该让E[N] = 0
所以需要逆序推出开始时的最小能量
由E[k+1] = 2*E[k]-H[k+1]可得
E[k] = (E[k+1]+H[k+1])/2

又因为能量是一个整数,有需要保证过程中能量不能为负数,故应该对中间值进行向上取整即E[k] = ceil((E[k+1]+H[k+1])/2.0)

C++代码实现

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

const int N = 1e5+10;
int H[N];

int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &H[i]);
    
    int e = 0;//终点时的能量
    for(int i = n; i >= 1; --i) {//逆序枚举
        e = ceil((e+H[i])/2.0);
    }
    printf("%d\n", e);
    
    return 0;
}

二分

首先考虑暴力做法,枚举初始时的能量e,再判断是否合法,找到一个最低的可以满足条件的能量值。

但是暴力做法,O(n2)的时间复杂度会超时,又考虑到E[k+1] = 2E[k]-H[k+1],当初始能量e可以使整个过程中不出现负数时,e+1, e+2,…… 也一定是可以使之成立的,枚举的整个初始能量序列满足单调性,既然满足单调性,那么就可以考虑进行二分。
二分查找的范围是0—能量的最大值105,目标是寻找序列中第一个满足check()条件的数。
此外,在实现check()函数的时候,若中间某项能量值低于0则一定是不合法的,但是若有一项高于最大值的时候,则后面的序列不必再判断也是合法状态,因为若E[k]>MAX,则E[k+1] = 2
E[k]-H, 由于h一定小于MAX,所以E[k+1]也一定是>MAX的,可以通过数学归纳法容易的得到整个序列中后面的数也一定是大于MAX的,一定是合法序列,不必再判断。

C++代码实现

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 1e5 + 10;
int num[N];
int n;

bool check(int beg) {
    //判断beg是否合法的开始
    LL temp = beg;
    for(int i = 0; i <n ; ++i ) {
        temp = temp*2 - num[i];
        if(temp < 0 ) return false;
        if(temp >= N) return true;
    }
    return true;
}


int main() {
    scanf("%d", &n);
    
    for(int i = 0; i < n; ++i) scanf("%d", &num[i]);
    int l = 0, r = N; 
    while(l < r) {
        int mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid+1;
    }
    printf("%d\n", l);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/mwl000000/article/details/108146629