dp算法篇Day10

"那坚守,某一刻化作乌有"


46、交错字符串

(1) 题目解析

        我们可以看出,s1,s2拼接后的字符串s3长度一定是一样的。并且s3中的子串一定是s1或s2当中的子串,因此要看s1、s2能否拼接成s3本质就是查找公共子序串。

(2) 算法原理

        

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int m = s1.size(),n = s2.size();
        if(m+n != s3.size()) return false;
        s1 = " " + s1,s2 = " " +  s2,s3 = " " + s3;
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        //初始化 s1为空串
        for(int j=0;j<=n;++j)
        {
            if(s2[j] == s3[j]) dp[0][j] = true;
            else break;
        } 

        // s2 为空串
        for(int i=0; i<=m; ++i)
        {
            if(s1[i] == s3[i]) dp[i][0] = true;
            else break;
        }

        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                dp[i][j] = (s1[i]==s3[i+j] && dp[i-1][j]) || (s2[j] == s3[i+j] && dp[i][j-1]);
            }
        }

        return dp[m][n];
    }
};


47、两个字符串最小的ASCII码值删除和

(1) 题目解析

(2) 算法原理

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int m =s1.size(),n = s2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                if(s1[i-1] == s2[j-1])
                {
                     dp[i][j] = max(dp[i][j],dp[i-1][j-1] + s1[i-1]);
                }
            }
        }

        int sum = 0;
        for(auto& s:s1) sum+=s;
        for(auto& s:s2) sum+=s;
        return sum - dp[m][n]*2;
    }
};


48、最长重复子数组 

(1) 题目解析

(2) 算法原理 

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();

        int ret = 0;
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(nums1[i-1] == nums2[j-1]) dp[i][j] = dp[i-1][j-1]+1;
                ret = max(dp[i][j],ret);
            } 
        }
        return ret;
    }
};


49、01背包

(1) 什么是背包问题?

        背包问题是一个经典的dp题型。

 

(2) 题目解析 

 

(3) 算法原理

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int N = 1024;
// 物品个数 、 背包容量
int n,V;
// 物品体积 物品价值
int v[N],w[N];
// dp表
int dp[N][N];

int main() {
    // 输入数据
    cin >> n >> V;
    // 录入物品体积和价值
    for(int i=1;i<=n;++i)
        cin >> v[i] >> w[i];

    // 初始化 + 第一个小问
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=V;++j)
        {
            dp[i][j] = dp[i-1][j]; 
            // 该位存在
            if(j-v[i] >= 0)
            {
                dp[i][j] = max(dp[i][j],dp[i-1][j-v[i]] + w[i]);
            } 
        }
    }
    // 输出结果
    cout << dp[n][V] << endl;

    memset(dp,0,sizeof dp);
    // 初始化:第二个小问
    for(int j=1;j<=V;++j) dp[0][j] = -1;
    
    // 填值
    for(int i=1;i<=n;++i)
        for(int j=1;j<=V;++j)
        {
            // 不选是一定存在的  即便j不存在 也是-1 不影响
            dp[i][j] = dp[i-1][j];
            if(j-v[i] >= 0 && dp[i-1][j-v[i]] != -1)
            {
                dp[i][j] = max(dp[i][j],dp[i-1][j-v[i]] + w[i]);
            }
        }
    
    cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;
    return 0;
}

        可以看出,背包问题的这两个条件,在状态转移方程上几乎是相似的,只是加上了一些限制条件。

优化:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int N = 1024;
// 物品个数 、 背包容量
int n,V;
// 物品体积 物品价值
int v[N],w[N];
// dp表
int dp[N];

int main() {
    // 输入数据
    cin >> n >> V;
    // 录入物品体积和价值
    for(int i=1;i<=n;++i)
        cin >> v[i] >> w[i];

    // 初始化 + 第一个小问
    for(int i=1;i<=n;++i)
    {
        // 从右往左
        for(int j=V;j>=v[i];--j)
        {
            dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    // 输出结果
    cout << dp[V] << endl;

    memset(dp,0,sizeof dp);
    // 初始化:第二个小问
    for(int j=1;j<=V;++j) dp[j] = -1;
    // 填值
    for(int i=1;i<=n;++i)
        // 从右往左
        for(int j=V;j>=v[i];--j)
        {
            if(dp[j-v[i]] != -1)
            {
                dp[j] = max(dp[j],dp[j-v[i]] + w[i]);
            }
        }
    
    cout << (dp[V] == -1 ? 0 : dp[V]) << endl;
    return 0;
}

 


50、分割等和子集

(1) 题目解析     

        我们换一个思路,该题目就是要让分成两个值相同的数组,也就意味着每一个数组的值都是原数组sum总和的一半。当sum / 2 商为奇数是,根本可能划分成两个值相等的数组。 因此,我们只需要求半边数组,求它的和为 sum / 2 即可。

        同样,我们遍历数组选数时,就是一种选与不选的"01背包问题",该题解的本质又是一种简单的背包模型。从nums的数组中,选取n个数,能让他的最终值 == sum / 2,不过此时没有所谓的背包容量的限制。

(2) 算法原理

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        int sum = 0;
        for(auto& e:nums) sum += e;
        if(sum % 2 !=0) return false;

        int aim = sum / 2;
        vector<vector<bool>> dp(n+1,vector<bool>(aim+1));
        // 初始化
        for(int i=0;i<=n;++i) dp[i][0] = true;

        for(int i=1;i<=n;++i)   
            for(int j=1;j<=aim;++j)
            {
                dp[i][j] = dp[i-1][j];
                if(j >= nums[i-1]) 
                    dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i][j];
            }
        return dp[n][aim];
    }
};

优化:

        我们同样可以将二位dp表降维后优化。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        int sum = 0;
        for(auto& e:nums) sum += e;
        if(sum % 2 !=0) return false;

        int aim = sum / 2;
        vector<bool> dp(aim+1);
        // 初始化
        dp[0] = true;
        for(int i=1;i<=n;++i)    
            for(int j=aim;j>=nums[i-1];--j) // 从右往左{ 
                dp[j] = dp[j-nums[i-1]] || dp[j];
            }
            
        return dp[aim];
    }
};


本篇到此结束,感谢你的阅读。

祝你好运,向阳而生~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值