leetcode 518 and 377

518:零钱兑换ⅠⅠ

思路一:母函数

1.假设有n种零钱,分别为c1,c2…cn,需要找零钱数为m。
2.对于多项式(1+xc1+x2c1+x3c1+x4c1+……+xi*c1)(1+xc2+x2c2+x3c2+x4c2+……+xj*c2)……(1+xcn+x2cn+x3cn+x4cn+……+xk*cn),其中i= ⌊ m / c 1 ⌋ \lfloor m/c1 \rfloor m/c1,j= ⌊ m / c 2 ⌋ \lfloor m/c2 \rfloor m/c2,k= ⌊ m / c n ⌋ \lfloor m/cn \rfloor m/cn,结果为1+a1xb1+a2xb2+a3xb3+a4xb4+……+anxbn
根据幂的相乘原理,结果aixbi中bi对应找零的总额,ai对应找零的方法总数。
3.所以我们只需要计算多项式乘积的结果,取得anxbn即可获得找零方法总数
4.循环遍历n次,每一次循环计算前i组多项式的乘积结果,记数组s[m]为为已经计算的i-1组多项式乘积的系数结果,t[m]为第i轮遍历完时,s[m]的临时结果。对于第i轮循环,我们需要更新系数数组t[m],其中 t [ i ] = ∑ i = 1 m s [ j + λ ∗ c i ] t[i]=\sum_{i=1}^ms[j+\lambda*ci] t[i]=i=1ms[j+λci],其中 j + λ ∗ c i = = i j+\lambda*ci==i j+λci==i
5.因为只需要算anxcn,所以只需要保留系数数组长度只需要m即可。

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> s(amount+1,0);
        vector<int> t(amount+1,0);
        for(int j=0;j<=amount;j++)
        {
            s[j]=j%coins[0]==0?1:0;
        }
        for(int i=1;i<coins.size();i++)
        {
            for(int j=0;j<=amount;j++)
            {
                for(int k=0;j+k<=amount;k+=coins[i])
                {
                    t[j+k]+=s[j];
                }
            }
            for(int j=0;j<=amount;j++)
            {
                s[j]=t[j];
                t[j]=0;
            }
        }
        return s[amount];
    }
};

思路二:动态规划

1.假设dp[i][j]代表使用coins[0]……coins[i]的硬币拼成j元价值的组合数。
2.状态迁移方程:
d p [ i ] [ j ] = ∑ k = 0 i d p [ k ] [ j ] + d p [ i − c o i n s [ i ] ] dp[i][j]=\sum_{k=0}^idp[k][j]+dp[i-coins[i]] dp[i][j]=k=0idp[k][j]+dp[icoins[i]]
3.实际上还可以进一步做状态压缩,记dp[j]为组成j元的组合数。
4. d p [ j ] = ∑ i = 0 n d p [ j − c o i n s [ i ] ] dp[j]=\sum_{i=0}^ndp[j-coins[i]] dp[j]=i=0ndp[jcoins[i]]
5.边界条件:dp[0]=1

代码:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
    int length=coins.size();
    vector<int> dp(amount+1,0);
    dp[0]=1;
    for(int i=0;i<length;i++)
    {
        for(int j=coins[i];j<=amount;j++)
        {
            dp[j]+=dp[j-coins[i]];
        }
    }
    return dp[amount];
    }
};

377:组合总和

思路一:

1.记dp[i]为组成大小为i的组合个数
2. d p [ j ] = ∑ i = 0 n d p [ j − n u m s [ i ] ] , n u m s [ i ] < = j < = t a r g e t dp[j]=\sum_{i=0}^ndp[j-nums[i]],nums[i]<=j<=target dp[j]=i=0ndp[jnums[i]]nums[i]<=j<=target
3.与上面518题不同的是,此题的内循环循环遍历为nums数组的元素,外层循环是遍历从1遍历到target,因此在一次外层循环时,所有dp[j-nums[i]]都被考虑到,也就是顺序也考虑进来。比如计算dp[4],其中nums数组有两个数1和3,那么如此计算dp[4]时,dp[1]和dp[3]都有被考虑在内。
4.计算时候要考虑某个dp值过大的情况

代码:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1);
        dp[0] = 1;
        for (int i = 1; i <= target; i++) {
            for (int& num : nums) {
                if (num <= i && dp[i - num] < INT_MAX - dp[i]) {
                    dp[i] += dp[i - num];
                }
            }
        }
        return dp[target];
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/combination-sum-iv/solution/zu-he-zong-he-iv-by-leetcode-solution-q8zv/

思路二:

1.1.记dp[i]为组成大小为i的组合个数
2. d p [ j ] = ∑ i = 0 n d p [ j − n u m s [ i ] ] , n u m s [ i ] < = j < = t a r g e t dp[j]=\sum_{i=0}^ndp[j-nums[i]],nums[i]<=j<=target dp[j]=i=0ndp[jnums[i]]nums[i]<=j<=target
3.在计算dp数组过程中可以进一步进行状态压缩,需要借组自动排序的map,自底向上计算dp[target]。
4.首先对nums进行排序,借助红黑树为底部的map(自动排序),加入节点(0,1)
5.每一次弹出红黑树最小的节点(x,y),遍历nums数组,如果以x+nums[i]作为key的节点存在则更新其value+=y,否则插入节点(x+nums[i],y)
6.加的过程注意数字太大问题

代码:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        map<int,int> dp;
        for(int i=0;i<nums.size();i++)
        {
            dp[nums[i]]=1;
        }
        map<int,int>::iterator it=dp.begin();
        while(it->first<target)
        {
            for(int i=0;i<nums.size();i++)
            {
                if(it->first+nums[i]>target)
                    continue;
                map<int,int>::iterator t=dp.find(it->first+nums[i]);
                if(t==dp.end())
                    dp[it->first+nums[i]]=it->second;
                else
                {
                    int temp1=t->second;
                    int temp2=it->second;
                    if(temp1<INT_MAX-it->second)
                        dp[t->first]=temp1+temp2;
                }
            }
            dp.erase(it++);
        }
        if(it->first==target)
            return it->second;
        else
            return 0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值