数据结构 : 动态规划学习一

动态规划学习一

动态规划,虽然抽象后进行求解的思路并不复杂,但具体的形式千差万别,找出问题的子结构以及通过子结构重新构造最优解的过程很难统一.

动态规划求解的一般思路

判断问题的子结构,也可以看做状态,当具有最优子结构时,动态规划可能适用.

求解重叠子问题.一个递归算法不断地调用调用同一个问题,递归UI可以转化为查表从而利用子问题的解.

分治法则不同,每次递归都产生新的问题,重新构造一个最优解.

备忘录法,是动态规划的一种变形,自顶向下的策略.更像递归算法,初始化表为一个特殊值,标记未填充,当递归算法第一次遇到子问题,计算并填表.而后每次遇到只需要直接从表中取值.

1 硬币找零问题

假设有几种硬币,如1,3,5并且数量无限.

请找出能够组成某个数目的找零所使用使用的最小硬币数

解法:

  • 假设硬币的种类由 coin[0…n-1] 数组表示.

  • 用待找零的数目k 描述 子结构/状态. 记作 sum[k]. 表示找零k所需的最小硬币数.

  • 最终需要求解的是 sum[total].

  • 那么递归公式有

    sum[k] = min(sum[k-coin[0],sum[k-con[1]…sum[k-coin[n-1])+1.

状态表示:

typdef struct{
    int nCoin; // 使用硬币数量
    // 以下两个成员为了便于构造出求解过程的展示
    int lastSum; // 上一个状态
    int addCoin; // 从上一个状态达到当前状态所需要的是哪种硬币
} state;
// 求解过程
state *sum = malloc(sizeof(state)*(total+1));
//init
for(i=0;i<=total;i++) 
    sum[i].nCoin = INF;
sum[0].nCoin = 0;
sum[0].lastSum = 0;

for(int i=1;i<=total;i++){
    for(int j=0;j<n;j++){
        
        if(i-coin[j]>=0&&sum[i-coin[j]].nCoin+1<sum[i].nCoin){
            sum[i].nCoin = sum[i-coin[j]].nCoin+1;
            sum[i].lastSum=j;
            sum[i].addCoin=coin[j];
        }
    }
}
if(sum[total].nCoin==INF){
    printf("can't make change.\n");
    return -1;
}
else{
    return sum[total].nCoin;
}

LeetCode 322. Coin Change

class Solution {
    public:
    int coinChange(vector<int>& coins, int amount) {
        // 将所需数量标记为最大
        vector<int> dp(amount + 1, INT_MAX);
        dp[0] = 0;// 总数为0,无需硬币
        for (int i = 1; i < amount + 1; ++i) {
            for (int j = 0; j < coins.size(); ++j) {
                if (i - coins[j] >= 0 && dp[i - coins[j]] < dp[i]/*注意是小于号*/) {
                    dp[i] = dp[i - coins[j]] + 1;
                }
            }
        }
        if (dp[amount] == INT_MAX) {
            return -1;
        } else {
            return dp[amount];
        }
    }
};

扩展

一个矩形区域被划分为N*M的小矩形格子,在格子(i,j)中有A[i,j]个苹果.

现在从左上角的格子(1,1)出发,要求每次只能向右走一步或向下走一步,最后到达(N,M),每经过一个格子就把其中的苹果全部拿走.

请找出能拿到最多(或最少)苹果数的路线.

LeetCode 64. Minimum Path Sum

class Solution {
    public:
    int minPathSum(vector<vector<int>>& grid) {

        if (grid.empty())
            return 0;
        int rows = grid.size();
        int cols = grid[0].size();

        // 第一行
        for (int i = 1; i < cols; ++i) {
            grid[0][i] += grid[0][i - 1];
        }
        // 第一列
        for (int i = 1; i < rows; ++i) {
            grid[i][0] += grid[i - 1][0];
        }
        // 累加
        for (int i = 1; i < rows; ++i) {
            for (int j = 1; j < cols; ++j) {
                grid[i][j] += min(grid[i - 1][j], grid[i][j - 1]);
            }
        }

        return grid[rows - 1][cols - 1];
    }
};

LeetCode 741. herry Pickup

这个题有点难.待补充

参考文献

常见动态规划问题分析与求解

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/86535996
今日推荐