动态规划算法OJ刷题(2)

CC88 不同路径的数目(一)

题目描述

一个机器人在m×n大小的地图的左上角(起点)。机器人每次可以向下或向右移动。机器人要到达地图的右下角(终点)。可以有多少种不同的路径从起点走到终点?

解题思路

  • 状态F(i, j) : 从(0, 0)到(i, j)的路径个数
  • 状态转移方程 : F(i-1, j) + F(i, j-1)
  • 初始状态 : F(i, 0) = F(0, j) = 1
  • 返回 : F(row-1, col-1)
class Solution {
public:
    /**
     * 
     * @param m int整型 
     * @param n int整型 
     * @return int整型
     */
    int uniquePaths(int m, int n) {
        // write code here
        vector<vector<int>> ret(m, vector<int>(n, 1));//m-row,n-col
        //初始条件 第一行 = 第一列 = 1
        for(int i = 0; i < m; ++i)
            ret[i][0] = 1;
        for(int i = 0; i < n; ++i)
            ret[0][i] = 1;
        //状态转移方程
        for(int i = 1; i < m; i++)
        {
            for(int j = 1; j < n; j++)
            {
                ret[i][j] = ret[i][j-1] + ret[i-1][j];
            }
        }
        return ret[m-1][n-1];
    }
};

CC86 带权值的最小路径和

题目描述

给定一个由非负整数填充的m x n的二维数组,现在要从二维数组的左上角走到右下角,请找出路径上的所有数字之和最小的路径。注意:你每次只能向下或向右移动。

解题思路

  • 状态F(i, j) : 从(0, 0)到(i, j)的最短路径和
  • 状态转移方程 : min(F(i-1, j) + F(i, j-1)) + array[i][j]
  • 初始状态 : F(0, 0) = array[0][0] F(i, 0) = F(i-1, 0) + array[i][0] F(0, j) = F(0, j-1) + array[0][j]
  • 返回 : F(row-1, col-1)
class Solution {
public:
    /**
     * 
     * @param grid int整型vector<vector<>> 
     * @return int整型
     */
    int minPathSum(vector<vector<int> >& grid) {
        // write code here
        vector<vector<int>> ret(grid);
        int row = grid.size();
        int col = grid[0].size();
        //初始条件
        for(int i = 1; i < row; i++)
            ret[i][0] = ret[i-1][0] + grid[i][0];
        for(int i = 1; i < col; i++)
            ret[0][i] = ret[0][i-1] + grid[0][i];
        //转移方程
        for(int i = 1; i < row; ++i)
        {
            for(int j = 1; j < col; ++j)
            {
                ret[i][j] = min(ret[i-1][j], ret[i][j-1]) + grid[i][j];
            }
        }【
        return ret[row-1][col-1];
    }
};

背包问题(0,1背包)

问题描述

有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值。问最多能装入背包的总价值是多大?

解题思路

状态范围索引是从1开始,价值数组和大小数组索引范围是从0开始,故第i个商品的容量为A[i-1],价值为V[i-1]

这里就要用到二维数组来表示价值和容量的关系。

情况1:放入第i个商品但是要取出某个商品 F(i, j) = F(i-1, j - A[i-1]) + V[i],j-A[i-1]表示容量要扣除掉第i个商品的体积【是第2种情况的子情况,因为对于背包的容量来说情况1和2都没超重】;

情况2:放入第i个商品 F(i, j) = F(i-1, j) + V[i]

情况3:不放入第i个商品,F(i, j) = F(i-1, j)【第i个商品放不下的时候,就会直接退化到上一个商品的状态值】

状态描述F(i, j) : 表示把第i个物品放入容量为j的背包中,此时该背包价值最大。

状态转移方程:如果A[i-1] > j ,表示情况3不放入第i个商品,F(i, j) = F(i-1, j);如果A[i-1] <= j,表示情况1和2放入第i个商品,F(i, j) = max(F(i-1, j), F(i-1, j - A[i-1]) + V[i-1]);

初始值:F(0, j) = 0 表示包有足够的容量,但是没有放入任何商品,价值为0;F(i, 0) = 0表示包容量为0,故价值为0

返回 : F(n, m)

class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @param V: Given n items with value V[i]
* @return: The maximum value
*/
int backPackII(int m, vector<int> A, vector<int> V) {
        //若无商品或者无背包,则直接返回0
        if(A.empty() || V.empty() || m < 1)
            return 0;
        //多加一行一列,给初始条件赋值
        const int N = A.size() + 1;//物品个数
        const int M = m + 1;//背包数
        //初始条件 F(i, 0) = F(0, j) = 0
        vector<vector<int>> ret(N, vector<int>(M, 0));
        //状态递推
        for(int i = 1; i < N; ++i)
        {
            for(int j = 1; j < M; ++j)
            {
                //第i个商品在A中对应的索引为i-1: i从1开始
                //如果第i个商品大于j,说明放不下, 所以(i,j)的最大价值和(i-1,j)相同
                if(A[i-1] > j) ret[i][j] = ret[i-1][j];
                else
                    ret[i][j] = max(ret[i-1][j], ret[i-1][j-A[i-1]] + V[i-1]);
            }
        }
    	return ret[N-1][m];
    }
};

//用一维数组实现,内内层循环要从后往前更新
const int N = A.size();//物品个数
const int M = m + 1;//背包数
vector<int> ret(M, 0);
for(int i = 1; i <= N; ++i)
{
    for(int j = M - 1; j > 0; --j)
    {
        if(A[i] <= j) ret[j] = max(ret[j], ret[j-A[i]] + V[i]);
        else ret[j] = ret[j];
    }
}
return ret[m];

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值