leetcode动态规划之零钱兑换问题

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。
输入: amount = 10, coins = [10] 
输出: 1

解题

Images

代码:

class Solution {
public:
    int change(int amount, vector<int>& coins) {

        int row=coins.size()+1;
        int col=amount+1;
     
        if(amount==0)
        return 1;
           if(row==1)
        return 0;
        vector<vector<int>> dp(row,vector<int>(col,0));
        for(int i=0;i<row;i++)
        dp[i][0]=1;

        for(auto a:dp[0])
        cout<<a;
        cout<<endl;
        for(int i=1;i<row;i++)
        {
            for(int j=1;j<col;j++)
            {
                if(j-coins[i-1]<0)
                dp[i][j]=dp[i-1][j];
                else
                dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]];

            }
        }
        for(auto b:dp[1])
        cout<<b;

        return dp[row-1][col-1];

    }
};

那么接下来如何进行状态压缩呢

我们发现只是和dp[i][…]以及dp[i-1][…]有关.所以压缩;

举个例子,我们将table[0][j]状态存在二维表的第0行,接着推出table[1][j]对应的值,并将table[1][j]的状态存放于二维表的第1行。再接着推出table[2][j]的状态时,table[0][j]的状态已经没有保存意义了,因此直接将table[2][j]的状态保存到二维表的第0行,并以此类推,使数组空间得到循环利用。

Images

有时候压缩也要注意遍历的方向,不可以影响后面的结果
比如
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

输入: [1, 5, 11, 5]

输出: true

解释: 数组可以分割成 [1, 5, 5] 和 [11].

class Solution {
public:
    int change(int amount, vector<int>& coins) {

        int row=coins.size()+1;
        int col=amount+1;
     
        if(amount==0)
        return 1;
           if(row==1)
        return 0;
        vector<int> dp(amount+1,0);
        dp[0]=1;

        for(int i=1;i<row;i++)
        {
            for(int j=1;j<col;j++)
            {
                if(j-coins[i-1]>=0)
                dp[j]=dp[j]+dp[j-coins[i-1]] ;        
            }
        }
       

        return dp[amount];

    }
};
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        //动态规划
        int sum=0;
        for(auto a:nums)
        sum+=a;
        if(sum%2!=0) return false;
        sum=sum/2;
        // int row=nums.size()+1;
        // int col=sum+1;
        // vector<vector<bool>> dp(row,vector<bool>(col,false));
        // for(int i=0;i<row;i++)
        // dp[i][0]=true;
        // cout<<dp[1][0];
        // for(int i=1;i<row;i++)
        // {
        //     for(int j=1;j<col;j++)
        //     {
        //        if(j-nums[i-1]<0) 
        //        dp[i][j]=dp[i-1][j];
        //        else
        //         dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]];
        //     }
        // }
        // return dp[row-1][col-1];
        vector<bool> dp(sum+1,false);
        dp[0]=true;
        for(int i=1;i<=nums.size();i++)
        {
            for(int j=dp.size();j>0;j--)// **为了不影响结果,所以选择遍历方向时候要适当改变**
            {
                if(j-nums[i-1]>=0)
                dp[j]=dp[j-nums[i-1]]||dp[j];
            }
        }
        return dp[sum];

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值