搜狐2017秋招研发工程师笔试 —— 袋鼠过河(贪心、动态规划、转为图)

来源:互联网 发布:淘宝怎么看别人日销量 编辑:程序博客网 时间:2024/05/17 02:42

题目

测试链接

一只袋鼠要从河这边跳到河对岸,河很宽,但是河中间打了很多桩子,每隔一米就有一个,每个桩子上都有一个弹簧,袋鼠跳到弹簧上就可以跳的更远。每个弹簧力量不同,用一个数字代表它的力量,如果弹簧力量为5,就代表袋鼠下一跳最多能够跳5米,如果为0,就会陷进去无法继续跳跃。河流一共N米宽,袋鼠初始位置就在第一个弹簧上面,要跳到最后一个弹簧之后就算过河了,给定每个弹簧的力量,求袋鼠最少需要多少跳能够到达对岸。如果无法到达输出-1 。

输入描述:

输入分两行,第一行是数组长度N (1 ≤ N ≤ 10000),第二行是每一项的值,用空格分隔。

输出描述:

输出最少的跳数,无法到达输出-1

示例1

输入
5
2 0 1 1 1

输出
4

我觉得网站的测试用例不全面,你们可以用下面我单独给出的样例再测一下自己的贪心算法:

输入:    14    5 1 2 0 4 2 0 2 0 4 0 0 1 1输出:    5

思路

方法一 转换为图求最短路径

把线性模型转换为有向图,判断是否存在源点到终点的最小路径即可,时间复杂度O(VE)。

N个木桩,编号 0 — N-1;

每一个木桩后面都可能跟着一个可达点集合,据此构建该点与其所有子节点的连接关系;

用 Bellman-ford 算法对该图以0号木桩为源点寻最小路径即可。

注意:因为到达木桩 N-1 时还不算过河,需要添加一个辅助木桩N作为终点

方法二 动态规划

明天再写…

方法三 贪心策略

只需要维护一个可达区间,复杂度O(n^2)。
① 从当前落点a,根据在该点可跳的最远步长得到可达集S
②从S中找下一跳能到达最远处的点Si
③令a = Si,重复①②步直到可达集S包含岸点,或无法继续更新可达集
例子如下:
14个点,每个点的可跳数如图
运行例子
上图符号▲为每次跳到的点,箭头指向的是它可达区间的右边界

代码

方法一 转换为图求最短路径

#include <iostream>#include <vector>#include <algorithm>#include <limits.h>using namespace std;int main() {    int N; cin >> N;    int *stone = new int[N + 1];    stone[N] = 0;// 辅助点,转换为图时需要该点作为终点,存在最小路径到该点则能过河    for (int i = 0; i < N; i++) cin >> stone[i];    // 构建邻接表, 时间复杂度O(V + E)    vector<vector<int> > map;    for (int curr_node = 0; curr_node < N + 1; curr_node++) {        vector<int> node;        for (int subNode = curr_node + 1; subNode <= curr_node + stone[curr_node] && subNode < N + 1; subNode++) {            node.push_back(subNode);        }        map.push_back(node);    }    // 用 Bellman - ford 找起始点到辅助终点的最小路径,时间复杂度O(VE)    vector<int> step(N + 1, INT_MAX);    step[0] = 0;    for (int i = 1; i < N + 1; i++) {        for (int node = 0; node < N + 1; node++) {            if (step[node] == INT_MAX) continue;            for (int sub_i = 0; sub_i < map[node].size(); sub_i++) {                int subNode = map[node][sub_i];                step[subNode] = min(step[subNode], step[node] + 1);            }        }    }    cout << (step[N] == INT_MAX ? -1 : step[N]) << endl;    return 0;}

方法三 贪心策略

#include <iostream>#include <vector>#include <algorithm>#include <limits.h> using namespace std;int main() {    // 初始化输入数据    int N = 0; cin >> N;    vector<int> stone(N);    for (int i = 0; i < N; i++) cin >> stone[i];    // 区间起点、区间右边界、新的区间起点、答案    int base, rightMost, new_base, res;     // 到达每个点的跳数    vector<int> step(N); step[0] = 0;    bool is_ok = false;    for ( base = 0, rightMost = stone[0]; base < N; base = new_base) {          // 更新可达区间        rightMost = new_base = base;        for (int i = base + 1; i <= base + stone[base] && i < N; i++) {             // 避免对之前已经能到达的点重复加步数            if(!step[i]) step[i] = step[base] + 1;            if (stone[i] && rightMost < i + stone[i]) {                new_base = i;                rightMost = i + stone[i];                 //已经能到河对岸,终止循环                if (rightMost >= N) {                    res = (i == N - 1 ?step[N - 1] + 1: step[new_base] + 1);                    is_ok = true;                    break;                }            }        }        // 过河        if (is_ok) break;        // 可达区间为 0 ,没法再继续跳        if (base == new_base) break;    }    cout << (is_ok? res: -1)<< endl;    return 0;}
原创粉丝点击