leetcode中可以用背包问题思想解题的题

这两天为了复习背包问题,回顾了我之前写的博客,并且找了几道leetcode的题目练练手。

leetcode上没有专门针对背包问题的题目,但有可以用背包思想解题的题。

 

1、Coin Change(Leetcode 322)

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.(You may assume that you have an infinite number of each kind of coin.)

Example 1:

Input: coins = [1, 2, 5], amount = 11

Output: 3

Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3

Output: -1

即给我们不同面值的硬币,每种硬币无数个,再给定一个总面值amount,问我们最少可以用多少个硬币组成总面值为amount.

思路:

硬币问题就像是背包问题,硬币的面值可以看做要放入背包的物品的体积/重量/价值等属性。

这道题中我们可以把要凑成的总面值看作一个背包的总容量,即我们的背包最多只能够放下重量为amount的东西。把每一个硬币的面值看作该硬币的重量,那我们就把问题转为,在一个总容量为amount的背包中放进最少个物品使得这些物品的总重量达到背包的最大容量。

根据要求的问题,我们可以设计一个数组bag[w]表示重量为w的背包最少可以放几件物品。则我们最终的答案就是bag[amount]。对于bag[w], 1<=w<=amount,这个我们需要计算的,且我们要取的是最小值,故可以将他们初始化为一个较大的值,我们可以让bag[w]=w,因为硬币的面值最小为1。对于bag[0],根据bag数组的定义可知,bag[0]=0。

接下来我们要决定怎么计算bag[w]。假设coin[i]表示第i个硬币的面值,那么bag[w]=min(bag[w],min(bag[w-coin[i]])+1)。我们可以这样理解,我们需要一个一个凑硬币,即需要一个一个地将物品放进背包。所以要让背包重量为W,我们需要在背包重量为W-coin[i]时放进重量为coin[i]的物品,故bag[W]=bag[W-coin[i]]+1(这里的1就是指这个重量为coin[i]的物品),对每个bag[W]都要取最小,就需要对每个bag[W-coin[i]]进行比较(这里的coin[i]指向不同重量的物品),然后取最小的那一个加一,再跟bag[w]比较,最后取最小值。

代码:

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        if(amount<1)
            return 0;
        vector<int> bag(amount+1,amount+1);
        bag[0]=0;
        for(int v=1;v<=amount;++v)
            for(int coin:coins)
                if(v-coin>=0)
                    bag[v]=min(bag[v],bag[v-coin]+1);
        return (bag[amount]==amount+1)?-1:bag[amount];
        
    }
};
 

2、Coin Change 2(Leetcode 518

You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Note:

You can assume that

  • 0 <= amount <= 5000
  • 1 <= coin <= 5000
  • the number of coins is less than 500
  • the answer is guaranteed to fit into signed 32-bit integer

Example 1:

Input: amount = 5, coins = [1, 2, 5]

Output: 4

Explanation: there are four ways to make up the amount:

5=5

5=2+2+1

5=2+1+1+1

5=1+1+1+1+1

思路:

这题就是把上一题的问题换成有多少种可以组成面值为amount的方案。上一题已经讲了那么多,这一题就直接说核心。

根据问题定义bag[w]表示要凑成总面值为w可以有几种方案。显然bag[w]=bag[w]+bag[w-coin[i]],因为如果为k种不同方案可以凑成总面值为w-coin[i],则在这k种不同的方案中再加一个coin[i]即得到总面值的w的方案。

代码:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        if(amount==0)
            return 1;
        vector<int> bag(amount+1,0);
        bag[0]=1;
        for(int coin:coins)
            for(int v=coin;v<=amount;++v)
                bag[v]+=bag[v-coin];
        return bag[amount];
    }
};

 

3、Partition Equal Subset Sum(Leetcode 416)

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

这道题就是给我们一个非空正整数序列,问我们能否把这个序列分成两个元素总和相等的集合。

思路:

为什么说这道题可以转为背包问题?我觉得可以这样理解:

(1)已知整个序列,那么我们可以由计算出每个集合的元素和是多少;=>背包的总容量cap

(2)要对每一个元素进行选择,决定元素要放在两个集合中的哪一个;=>物品的两种状态:放进背包与不放进背包

所以问题转化为将能否将n个物品中的部分物品放进背包中使得背包总容量为cap。

设定数组bag[w]表示容量为w的背包可以放进价值为多少的物品,然后将序列中的每个元素ele视为重量和价值都相同的物品。

代码:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=accumulate(nums.begin(),nums.end(),0);
        if(sum&1)
            return false;
        sum=(sum>>1);
        vector<int> bag(sum+1,0);
        for(int w:nums)
            for(int v=sum;v>=w;--v)
                if(bag[v]<bag[v-w]+w)
                    bag[v]=bag[v-w]+w;
        return bag[sum]==sum;
    }
};

为了保证每件物品只被用一次,我们需要从v=sum开始计算。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值