掘金币编程问题随笔

今天遇到了一个问题,因为好久没刷题了,导致不那么熟悉。于是写下来记录一下解决过程。文末附可执行程序。

问题:

        一个玩家从某一村庄出发,开始挖金币。已知,玩家只能向右或者向下走,不能向左走或者向右走。此外,还有一个数组,存储每个金币的深度。村庄的位置在数组中的第一个金币的上方。求玩家能获得的最大金币数。

例:存储金币深度的数组 depth = [ 2 , 3 , 1 , 4 , 4], 则玩家最多能获得4枚金币。如下图。

递归解法

首先我尝试了暴力解法,即每次要么选择当前位置的金币,更改深度,要么维持当前深度,继续前进。针对时间复杂度高的问题,采用了记忆化搜索,使用dp数组记录状态,避免重复访问。但是数据量大时,开辟的数组可能会造成溢出。

非递归解法

然后,我将暴力解法变成了循环实现。并且通过两个一维数组prev 和curv 交替记录状态,避免了使用二维数组。将n*m的空间复杂度 变成了 2*m,其中n是金币个数,m是金币所埋藏的最大深度 。注意下面这段程序中,第二个for循环的初始条件那里笔误,应该是int dep = max_d。v数组是存储金币深度的数组。


后来发现,通过选择从深度由大到小的赋值顺序,可以只用一个数组解决。至此,该方法的空间复杂度进一步降低到 o(m )。时间复杂度是o (n*m)。

完整的可执行程序如下

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> depth; //存储金币数组

    int n; //输入金币数
    cin >> n;
    for (int i = 0; i < n; ++i) {  //依次输入每个金币的深度
        int tmp; 
        cin >> tmp;
        depth.push_back(tmp);
    }

    //查找最大的深度
    int max_depth = 0;
    for (int i = 0; i < depth.size(); ++i) {
        if (depth[i] > max_depth) max_depth = depth[i];
    }

    //边界条件是0
    vector<int> curv(max_depth + 5, 0);

    for (int p = n - 1; p >= 0; --p) {
        for (int dep = max_depth; dep >= 0; --dep) {

            if (depth[p] == dep) {
                curv[dep] = 1 + curv[depth[p]];
            }
            else if (depth[p] > dep) {
                curv[dep] = curv[depth[p]] > curv[dep] ? curv[depth[p]] : curv[dep];
            }
            else {
                continue;
            }
        }

    }
    cout << curv[0] << endl;;
}

上述解法对于本题来讲实际上是冗余的,即题目只要求从村庄出发能拿到的金币数。但本方法给出了从任意位置出发的能拿到的最大金币数。优化的思路可以从这里开始。

进一步的改善方法我考虑了通过一些数据结构维持p+1位置的金币数,这个等待以后有时间再说吧。

不过我的思路大概是,通过只拿需要的数据来简化那个长度为m的数组。如下图。金币数组是 {2 3 1 4 4}, 绿色数字标出的是从该位置出发能获得的最大金币数,则根据图片所示,可以确定从村庄出发能获得的最大金币数就是 (1,1)坐标处的值,即 4 个。

我的打算是用滑动窗口来解决,不过暂时发现,这个方法比我想象的要复杂,至于能否找到线性时间复杂度的方法,等以后闲下来再说吧,先搞毕业论文了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值