dp算法篇Day10

dp算法篇Day10_第1张图片

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


46、交错字符串

(1) 题目解析

dp算法篇Day10_第2张图片

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

(2) 算法原理

        dp算法篇Day10_第3张图片

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> dp(m+1,vector(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) 题目解析

dp算法篇Day10_第4张图片

(2) 算法原理

dp算法篇Day10_第5张图片

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int m =s1.size(),n = s2.size();
        vector> dp(m+1,vector(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) 题目解析

dp算法篇Day10_第6张图片

(2) 算法原理 dp算法篇Day10_第7张图片

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

        int ret = 0;
        vector> dp(m+1,vector(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题型。

dp算法篇Day10_第8张图片

 

(2) 题目解析 dp算法篇Day10_第9张图片

 

(3) 算法原理

dp算法篇Day10_第10张图片

#include 
#include 
#include 
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;
}

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

优化:

dp算法篇Day10_第11张图片dp算法篇Day10_第12张图片

#include 
#include 
#include 
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) 题目解析     dp算法篇Day10_第13张图片

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

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

(2) 算法原理

dp算法篇Day10_第14张图片

class Solution {
public:
    bool canPartition(vector& 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> dp(n+1,vector(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& 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 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];
    }
};


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

祝你好运,向阳而生~

dp算法篇Day10_第15张图片

 

你可能感兴趣的:(dp动规算法,算法,leetcode)