import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param cost int整型一维数组 
     * @return int整型
     */
    public int minCostClimbingStairs (int[] costs) {
        // write code here
        // 算法核心:从【递归回溯尝试】优化到【记忆化搜素】(因为装填转移只有2个,因此效率与动态规划类似)
        // dp表默认初始化全-1
        int[] dp = new int[costs.length + 1];
        for (int i = 0; i < dp.length; i++) {
            dp[i] = -1;
        }
        // 注意,爬到数组刚好越界才算爬完
        int result = process(costs, costs.length, dp);
        return result;
    }

    // 计算爬到第target阶(0开始)的最小花费
    public int process (int[] costs, int target, int[] dp) {
        // 递归出口
        if (target == 0) {
            // 爬到第0阶台阶的最小花费
            // 为0,因为可以直接从0开始爬
            dp[0] = 0;
            return dp[0];
        }
        if (target == 1) {
            // 爬到第1阶台阶的最小花费
            // 为0,因为可以直接从1开始爬
            dp[1] = 0;
            return dp[1];
        }

        // 分支尝试

        // 本题状态的转移是【有限个】的,即【不依赖for循环】列举进行状态转移
        // 因此,【记忆化搜索】与【经典动态规划】的时间复杂度【近似】

        // 如果dp表中有计算好的值,则可直接返回
        if (dp[target] != -1) {
            return dp[target];
        }
        // 上了一个台阶到达target位置
        if (dp[target - 1] == -1) {
            // dp表中无值
            dp[target - 1] = process(costs, target - 1, dp);
        }
        int one = dp[target - 1] + costs[target - 1];
        
        // 上了两个台阶到达target位置
        if (dp[target - 2] == -1) {
            // dp表中无值
            dp[target - 2] = process(costs, target - 2, dp);
        }
        int two = dp[target - 2] + costs[target - 2];

        // 记录本位置dp值
        dp[target] = Math.min(one, two);
        return dp[target];
    }
}