动态规划

70. 爬楼梯

题意:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

实例:

算法练习-day36_动态规划

思路:本题我们之前也讲解过,实在有点简单,还不会的朋友,可以看我第一期的动态规划算法练习

C++代码:

int climbStairs(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1,dp[1]=1;
        for(int i=2;i<=n;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }

322. 零钱兑换

题意:给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

实例:

算法练习-day36_动态规划_02

思路:本题我们还是使用动态规划的思想,先去创建一个数组dp[amount+1],每个位置表示该大小下的最小硬币个数;并且在初始化时,给0号位置赋0,其他位置变为INT_MAX-1,这里本来可以使用INT_MAX,但是在测试用例中系统会去计算INT_MAX+1,越界了,因此我们给出INT_MAX-1。

然后在来说下递推公式,这个就比较简单了,每个元素无限次使用,因此背包和物品的遍历顺序无所谓;公式为dp[i]=min(dp[i],dp[i-coins[j]]+1)

最后,当dp数组的最后一个元素改变时,就说明有符合条件的元素集合,否则没有符合条件的元素集合,返回-1.

C++代码:

int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX-1);//凑足总额为n所需钱币的最少个数为dp[n]
        dp[0]=0;
        for(int i=0;i<=amount;i++)
        {
            for(int j=0;j<coins.size();j++)
            {
                if(i-coins[j]>=0)
                {
                    dp[i]=min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        return dp[amount]==INT_MAX-1?-1:dp[amount];
    }

279.完美方块

题意:给你一个整数 ,返回 和为 n 的完全平方数的最少数量 。n完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,、、 和 都是完全平方数,而 和 不是。14916311

实例:

算法练习-day36_动态规划_03

思路:本题和上一道题可以说是一模一样,只有两个区别:

  1. 目标值n必定有解
  2. 我们需要遍历的硬币方从1到根号n了,更加简单了

代码方面也和上道题一样,直接上代码!!!

C++代码:

int numSquares(int n) {
        vector<int> dp(n+1,INT_MAX-1);
        dp[0]=0;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=sqrt(n);j++)
            {
                if(i-j*j>=0)
                {
                    dp[i]=min(dp[i],dp[i-j*j]+1);
                }
            }
        }
        return dp[n];
    }

139.分词

题意:给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

实例:

算法练习-day36_动态规划_04

思路:本题我们使用完全背包的思想,先创建一个动态规划数组dp[s.size()+1],dp[i]表示s字符串长度i以及之前的元素都可以由字典中的元素组成。

开始先将数组初始化为false,然后对0长度的位置设置为true,我们判断[i,j]这个区间的元素可以找到的条件是:

  1. (i,j]组成的元素可以在字典中找到
  2. dp[i]必须被标记为true

满足以上两点dp[j]才能被标记true。动态规划的思想也就在这里,前免得判断结果会影响到后面的判断结果。

C++代码:

bool wordBreak(string s, vector<string>& wordDict) {
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;
        for(int i=1;i<=s.size();i++)
        {
            for(int j=0;j<i;j++)
            {
                string ss=s.substr(j,i-j);
                if(find(wordDict.begin(),wordDict.end(),ss)!=wordDict.end()&&dp[j]==true)
                {
                    dp[i]=true;
                }
            }
        }
        return dp[s.size()];
    }