动态规划2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chent86/article/details/83958510

leetcode 97

 思路:

一开始考虑怎么用动态规划解决问题,反而没什么头绪。于是改变思路,先不管动态规划,而是想如何解决这道题。

可以肯定的是,如果s3的最后一个字符与s1和s2的最后一个字符都不同,那么将无法通过s1与s2的交错得到s3。假若s1与s2其中一个的最后一个字符与s3的最后一个字符相同(假设是s1,删除最后一个字符得到s1*,而s3删除最后一个字符得到s3*)。那么问题将转化为s1*,s2与s3*的子问题。不过如果简单地从后往前删除,如果过程中s1、s2、s3的最后一个字符都相同时,选择删除s1或者删除s2的最后一个字符可能产生不同的结果,这里可以采用递归,也可以采用动态规划进行解决。

采用动态规划:

dp[i][j表示s1的前i个字符与s2的前j个字符能否通过交错得到s3的前i+j个字符,为0表示不能,为1表示能。

转换方程:

如果s1的第i个字符与s3的第i+j个字符相同,就执行:

dp[i][j] = dp[i-1][j]

如果s2的第j个字符与s3的第i+j个字符相同,就执行:

扫描二维码关注公众号,回复: 4219994 查看本文章

dp[i][j] = dp[i][j-1]

这两个操作可以都执行。然后还需要注意边界情况。

代码如下:

bool isInterleave(string s1, string s2, string s3) {
    int a = s1.length();
    int b = s2.length();
    int c = s3.length();
    if(a+b != c)
        return false;
    int dp[a+1][b+1] = {0};
    for(int i = 0; i <= a; i++) {
        if(s1.substr(0, i) == s3.substr(0, i))
            dp[i][0] = 1;
        else
            dp[i][0] = 0;
    }
    for(int i = 0; i <= b; i++) {
        if(s2.substr(0,i) == s3.substr(0,i))
            dp[0][i] = 1;
        else
            dp[0][i] = 0;
    }
    for(int i = 1; i <= a; i++)
    for(int j = 1; j <= b; j++) {
        if(s1[i-1] == s3[i+j-1] && dp[i-1][j] == 1)
            dp[i][j] = 1;
        if(s2[j-1] == s3[i+j-1] && dp[i][j-1] == 1)
            dp[i][j] = 1;
    }
    return dp[a][b] == 1;
}

leetcode 115

思路:

首先是想到使用递归的方法,T*为当前T删除第一个字符后的字符串,集合S*为S中以T的第一个字符开头直至S的末尾的字符串的集合(例如当T为bbit时, T*为bit, S*为{bbit, bit, it})。也就将原问题化解成T*与S*中的串的子问题,当子问题能成功结束,计数加一。最终的计数即为答案。

但似乎这个递归方法不容易转换为动态规划。换个思路,采用动态规划:

设dp[i][j]表示S的前i个字符组成的字符串中与T的前j个字符组成的字符串相同的子序列的个数,

则不论如何都有:dp[i][j] >=  dp[i-1][j] ,当s[i-1] != t[j-1]时取等号。

而如果s[i-1] == t[j-1],那么还需要在原来的基础上加上dp[i-1][j-1],这就等价于不使用原来S中的一个值来对应t[j-1],而是采用s[i-1]这个新的值。

代码如下:

int numDistinct(string s, string t) {
    int a = s.length();
    int b = t.length();
    if(a < b)
        return 0;
    int dp[a+1][b+1] = {0};
    for(int i = 0; i <= b; i++)
        dp[0][i] = 0;
    for(int i = 0; i <= a; i++)
        dp[i][0] = 1;          // dp[0][0] = 1
    for(int i = 1; i <= a; i++)
    for(int j = 1; j <= b; j++) {
        if(i < j) {
            dp[i][j] = 0;
            continue;
        }
        if(s[i-1] == t[j-1])
            dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
        else
            dp[i][j] = dp[i-1][j];
    }
    return dp[a][b];
}

leetcode 123

思路:

限制了只能进行两次交易,求出所有的增长段,找出最大的两笔交易这种贪心算法并不能得到答案。(当然,如果没有限制交易次数,这样无疑时最优的)。那么考虑使用动态规划的思想:

 dp[i][j]表示在前j+1天中交易i次得到的最大收益。

那么可以分为两种情况:

第j天不交易

dp[i][j] = dp[i][j-1]

第j天交易

dp[i][j] = max(price[j]-price[k]+dp[i-1][k-1])

其中在第k天买入了这支股票

也即

dp[i][j] = max(dp[i][j-1], max(price[j]-price[k]+dp[i-1][k-1]))

代码如下:

int maxProfit(vector<int>& prices) {
    int size = prices.size();
    if(size == 0 || size == 1)
        return 0;
    int dp[3][size] = {0};

    for(int i = 0; i <= 2; i++)
        dp[i][0] = 0;

    for(int i = 0; i < size; i++)
        dp[0][i] = 0;

    for(int i = 1; i <= 2; i++)
    for(int j = 1; j < size; j++) {
        int t = 0;
        for(int k = 0; k <= j; k++)
            t = max(prices[j]-prices[k]+dp[i-1][k], t);
        dp[i][j] = max(dp[i][j-1], t);
    }
    return dp[2][size-1];
}

然而由于重复计算,最后一个样例会超时,需要优化:

int maxProfit(vector<int>& prices) {
    int size = prices.size();
    if(size == 0 || size == 1)
        return 0;
    int dp[3][size] = {0};

    for(int i = 0; i <= 2; i++)
        dp[i][0] = 0;

    for(int i = 0; i < size; i++)
        dp[0][i] = 0;

    for(int i = 1; i <= 2; i++) {
        int t = prices[0];
        for(int j = 1; j < size; j++) {
            t = min(t, prices[j]-dp[i-1][j-1]);
            dp[i][j] = max(dp[i][j-1], prices[j]-t);
        }
    }
    return dp[2][size-1];
}

猜你喜欢

转载自blog.csdn.net/chent86/article/details/83958510