0
点赞
收藏
分享

微信扫一扫

【每日一题见微知著】DP算法——零钱和问题+元音字母序列统计

1220. 统计元音字母序列的数目—Hard

题目描述:

给你一个整数 n,请你帮忙统计一下我们可以按下述规则形成多少个长度为 n 的字符串:

  • 字符串中的每个字符都应当是小写元音字母('a', 'e', 'i', 'o', 'u'
  • 每个元音 'a' 后面都只能跟着 'e'
  • 每个元音 'e' 后面只能跟着 'a' 或者是 'i'
  • 每个元音 'i' 后面 不能 再跟着另一个 'i'
  • 每个元音 'o' 后面只能跟着 'i' 或者是 'u'
  • 每个元音 'u' 后面只能跟着 'a'

由于答案可能会很大,所以请你返回 模 10^9 + 7 之后的结果。

解题方法:

很基础的动态规划方法,状态为当前字母结尾的字符的可能组合数,比如长度为2的以a为结尾的组合,ea、ia、ua三个,则dp[2] [‘a’]=3;由于不需要保存中间状态,直接写为dp[‘a’]=3——由题意可知,状态的推导关系,长度为2的a结尾可以用长度为1的e、i、u结尾添加a组成,既有dp[‘a’]=pre_dp[‘e’]+pre_dp[‘i’]+pre_dp[‘u’]

在这里插入图片描述

代码实现:

class Solution {
    public final static int MOD=1000000000+7;
    
    public int countVowelPermutation(int n) {
        //状态
        int[] ans=new int[5];
        //前状态
        int[] mid=new int[5]; 
        
        //dp初始化
        for(int i=0;i<5;i++){
            ans[i]=1;
        }
        n-=1;
        
        while(n-->0){
            //dp推导规则,由前一个dp状态推导下一个状态
            mid[0]=((ans[1]+ans[2])%MOD+ans[4])%MOD;
            mid[1]=(ans[0]+ans[2])%MOD;
            mid[2]=(ans[1]+ans[3])%MOD;
            mid[3]=ans[2];
            mid[4]=(ans[2]+ans[3])%MOD;
            
            for(int i=0;i<5;i++){
                ans[i]=mid[i];
            }
        }
        //结果为长度为n的5种可能结尾之和
        int ans_=0;
        for(int i=0;i<5;i++){
            ans_=(ans[i]+ans_)%MOD;
        }
        return ans_;
    }
}

动态规划(DP)

  • 现实问题:在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果(比如:
  • 多阶段决策:当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线.这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策问题
  • 动态规划:在多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化的过程为动态规划方法

满足上述描述的多阶段问题可以使用动态规划步骤求解——自底向上的过程可以使用迭代推导,自顶向下可以使用递归求解

在这里插入图片描述

322. 零钱兑换-Mid

题目描述

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

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

解题方法

由于某一个数量的金额可以有前一个金额加对应的Coin组合得到——设置状态数组为金额数量i——dp[i]=min(dp[i-coins[index]]+1);判断取得最小的组合即可,局部dp最优推导全局最优

代码实现:

class Solution {
    public int coinChange(int[] coins, int amount) {
        //如果需要找零的数量为0,那么不需要硬币即可
        if(amount==0)
            return 0;
        
        int coins_l=coins.length;
        int[] dp=new int[amount+1];
        
        for(int i=0;i<=amount;i++){
            for(int c=0;c<coins_l;c++){
                //推导过程——dp[i]=min(i-coins[0:coins_l-1])
                int mid=i-coins[c];
                //刚好由一个硬币组成
                if(mid==0){
                    dp[i]=1;
                }
                //推导过程防止数组越界,不能组合用0表示(可以考虑用极大值表示)
                if(mid>0&&dp[mid]!=0&&(dp[i]>dp[mid]+1||dp[i]==0)){
                    dp[i]=dp[mid]+1;
                }
                
            }
        }
        //输出结果
        if(dp[amount]==0){
            return -1;
        }
        else{
            return dp[amount];
        }
    }
}

结尾

举报

相关推荐

0 条评论