动态规划算法__1

文章介绍了使用动态规划方法解决两个经典问题:最长回文子序列和打家劫舍。在最长回文子序列问题中,通过状态转移方程确定dp[i][j],并从后向前遍历。在打家劫舍问题中,分别考虑偷与不偷第一家的情况,更新dp数组以找到最大收益。
摘要由CSDN通过智能技术生成

最长回文子序列

解题思路

dp[i][j]含义:表示 s 的第 i 个字符到第 j 个字符组成的子串中,最长的回文序列长度是多少。

状态转移方程:如果 s 的第 i 个字符和第 j 个字符相同的话,那么dp[i][j] = dp[i+1][j-1] + 2;

​ 如果 s 的第 i 个字符和第 j 个字符不同的话,那么dp[i][j] = max(dp[i+1][j],dp[i][j-1]

遍历顺序:i 从最后一个字符开始往前遍历,j 从 i + 1 开始往后遍历,这样可以保证每个子问题都已经算好了。

初始化:i == j 时dp[i][j] =1(单个字符的最长回文序列是 1)

代码实现

int longestPalindromeSubseq(char * s){
    int n,i,j;
    n = strlen(s);
    if(n == 1){
        return n;
    }
    int max = 1;
    int dp[1001][1001]={0};
    //dp[i][j]表示第i个字符到第j个字符之间的回文子序列的长度
    for(i = 1;i < n+1;i ++){
        dp[i][i] = 1;
    }
    /**dp含义、递推公式、初始化、遍历顺序
    s[i] == s[j]时,那么dp[i][j] = dp[i+1][j-1] + 2;
    s[i] != s[j]时,dp[i][j] = max(dp[i+1][j],dp[i][j-1]
    
    */
    for(i = n;i >= 1;i--){
        for(j = i+1;j <= n;j++){
            if(s[i-1] == s[j-1]){
                dp[i][j] = dp[i+1][j-1] + 2;
            }else{
                // dp[i][j] = fmax(dp[i+1][j],dp[i][j-1]);
                if(dp[i+1][j] > dp[i][j-1]){
                    dp[i][j] = dp[i+1][j];
                }else{
                    dp[i][j] = dp[i][j-1];
                }
            }
            if(max < dp[i][j]){
            max = dp[i][j];
        }
        }
        // max = fmax(max,dp[i][j]);
        
    }
   return max;
}

打家劫舍_1

解题思路:

dp[i]含义:dp[i]表示前i步能偷的最大金额

状态转移方程:dp[i] = max(dp[i-2],dp[i-3])+ nums[i];每家必偷,再加上上一次偷的最大值

初始化:dp[0] = nums[0],dp[1] = nums[1],dp[2] = nums[2] + nums[0];

代码实现:

class Solution {
    int max(int a,int b){
        if(a > b)return a;
        else return b;
    }
    public int rob(int[] nums) {
        int[] dp = new int [1001];
        int m = 0;
        int n = nums.length;
        dp[0] = nums[0];
        m = max(dp[0],m);
        if(n == 1){
            return m;
        }
        dp[1] = nums[1];
        m = max(dp[1],m);
        if(n == 2){
            return m;
        }
        dp[2] = nums[2] + nums[0];
        m = max(dp[2],m);
        if(n == 3){
            return m;
        }
        for(int i = 3;i < n;i++){
            dp[i] = max(dp[i-2],dp[i-3])+nums[i];
            m = max(dp[i],m);
        }
        return m;
    }
}

打家劫舍_2

在打家劫舍_1的基础上添加了限制条件,nums[0],nums[n-1]相邻

解题思路

改进点:当偷第一家时,dp数组在n-2就结束循环;

不偷第一家时,dp数组从dp[1]开始初始化,在dp[n-1]结束循环

找出最大的金额

边界处理:当数组只有三个时,取出最大值即可

代码实现

class Solution {
    int max(int a,int b){
        a = a > b ? a : b;
        return a;
    }
    public int rob(int[] nums) {
        int[] dp = new int [1001];
        int n  = nums.length;
        int m;
        if(n == 1){
            return nums[0];
        }
        m = max(nums[0],nums[1]);
        if(n == 2){
            return m;
        }
        if(n == 3){
            return max(nums[2],m);
        }
        dp[0] = nums[0];
        dp[1] = nums[1];
        dp[2] = max ,nums[1]);
        m = max(dp[2],m);
        for(int i = 3;i < n-1;i++){
            dp [i] = max(dp[i-2],dp[i-3])+nums[i];
            m = max(m,dp[i]);
        }
        int s;
        dp[1] = nums[1];
        dp[2] = nums[2];
        s = max(nums[1],nums[2]);
        dp[3] = max(nums[1] + nums[3],nums[2]);
        s = max(dp[3],s); 
        for(int i=4;i<n;i++){
            dp [i] = max(dp[i-2],dp[i-3])+nums[i];
            s = max(s,dp[i]);
        }
        m = max(m,s);
        return m;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值