DP基本问题总结(2018.5.29 2018.6.9 2018.8.9更新)

参考文献:

【1】https://segmentfault.com/a/1190000006325321

【2】https://blog.csdn.net/q623702748/article/details/51297949

一晃就过去了两个月,提前退出了网易游戏的实习,前途未卜。但不管怎么样,生活还是要继续。DP问题还是在笔试里考得多些,还要记录笔试DP的题目。

网易笔试:合唱团:https://www.nowcoder.com/practice/661c49118ca241909add3a11c96408c8?tpId=85&tqId=29830&tPage=1&rp=1&ru=/ta/2017test&qru=/ta/2017test/question-ranking

这个题目笔试确实超出了我现在能力范围了,就复习一下看个热闹吧。。

此题的思路是令dp[k][i]表示在前i个学生(必须以第i个学生结尾)中选k个让乘积最大,由于学生的能力值有正有负,我们需要同时维护两个数组,dp_max和dp_min,分别表示最大和最小乘积。

dp_max[k][i] = max(dp_max[k][i], max(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));

是说以第i个学生结尾选k个学生,即dp_max[k][i],它的更新公式是取k-1个学生,以之前符合条件的任意j为结尾,即

dp_max[k-1][j] 去乘第i个学生的能力值,或者dp_min[k-1][j] * arr[i],这三者取最大值。这里j是有范围限制的,即要<i,同时根据题意又不能间距超过d,故有条件i - j <= d。

ans = max(ans, dp_max[K][i])最后我们的答案是在i从1~N取值中遍历得到的。

原来特么的可以插入代码啊。。。我日昍晶

#include<iostream>
#include<vector>
using namespace std;
inline long long max(long long a, long long b) { return (a>b ? a : b); }
inline long long min(long long a, long long b) { return (a>b ? b : a); }
int main()
{
	int N; cin >> N;
	vector<long> arr; arr.push_back(0); int temp, count = 0;
	while (count < N  && cin >> temp) {
		arr.push_back(temp);
		count++;
	}
	int K; cin >> K; int d; cin >> d;
	vector<vector<long>> dp_max; 
	dp_max.resize(K + 1);
	for (int i = 0; i < K + 1; i++){
		dp_max[i].resize(N + 1);
	}
	vector<vector<long>> dp_min(dp_max); long ans = 0;
	for (int i = 1; i <= N; i++){
		dp_max[1][i] = arr[i]; dp_min[1][i] = arr[i];
		for (int k = 2; k <= K; k++)
		{
			for (int j = i - 1; j > 0 && i - j <= d; --j) {
				dp_max[k][i] = max(dp_max[k][i], max(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));
				dp_min[k][i] = min(dp_min[k][i], min(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));
			}
		}
		ans = max(ans, dp_max[K][i]);
	}
	cout << ans << endl;
}

5. Longest Palindromic Substring

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

此题是比较经典的求最长回文串的题目,此题有一个比较有趣的思路是反转string s,找最长公共串,但是如果遇到例如“abcdgcba”这种自带部分回文的串就不行了,得到“abc”显然不对(有后续改进但我不想看了,有时间再补上)。

此题DP的的思路是设置dp[i][j]表示index i~j的子串是否是回文串,是为true否为false。

显然dp[i][i]均为true,同时我们可以根据串s来更新dp[i][i+1]的值:

dp(i,i+1)=(S​i​​==S​i+1​​)

初始值求完后,更新公式为:

dp(i,j)=(dp(i+1,j−1) and S​i​​==S​j​​)

这题目真的不难,奈何太久没做DP了,不会写。

This yields a straight forward DP solution, which we first initialize the one and two letters palindromes, and work our way up finding all three letters palindromes, and so on...

class Solution {
public:
    string longestPalindrome(string s) {
        string resErr;
        int len = s.size();
        if (len == 0)
            return resErr;
        vector<vector<int>> dp;
        dp.resize(len);
        for (int i = 0; i < len; i++) {
            dp[i].resize(len);
        }
        int begin = 0, end = 0;
        for (int i = 0; i < len; i++) {
            dp[i][i] = 1;
        }
        for (int i = 0; i < len - 1; i++) {
            if (s[i] == s[i + 1]) {
                dp[i][i + 1] = 1;
                begin = i; end = i + 1;
            }
        }
        for (int i = 2; i < len; i++) {
            for (int j = 0; j < len - i; j++) {
                if (s[j] == s[j + i] && dp[j + 1][j + i - 1] == 1) {
                    dp[j][j + i] = 1;
                    begin = j; end = j + i;
                }
            }
        }
        string res(s.substr(begin, end - begin + 1));
        return res;
    }
};

此篇由来:昨天(2018.5.28)被leetcode312那道hard难度的DP虐了以后,今天给了个基本题安慰了一下,所以顺手把一些DP的基本问题做过总结,水过今天的编程:) 首先看今天(2018.5.29)遇到的这一题:

70. Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

此题非常简单,既可以看作一个斐波那契数列,也可以当成最基础的DP问题(需要注意自顶向上计算的效率较高)。

dp[i]表示台阶数为i时的路径数,状态转移方程为dp[i] = dp[i-1] + dp[i-2],代码太简单了就不贴了。

LintCode 92.Backpack

Description

Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?

Example

If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], so that the max size we can fill this backpack is 10. If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack.

You function should return the max size we can fill in the given backpack.

Challenge

O(n x m) time and O(m) memory.

O(n x m) memory is also acceptable if you do not know how to optimize memory.

此题是一道最基本的背包问题,状态量dp[i][j]表示只允许拿前i个物品(0~i-1)中的任意个,背包容量为j时能达到的最大容量,很显然,状态转移方程为

以m=10,A=[2,3,5,7]为例,状态转移表如下所示,max那一项的含义是:对于第i个物品,我如果不拿,那么dp[i][j]的值还是dp[i-1][j]的值

代码如下,当时写的废话太多了:),代码的空间复杂度是O(nm),关于空间优化的问题请往下看。

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    int backPack(int m, vector<int> &A) {
        // write your code here
        vector<vector<int>> f;
        f.resize(A.size()+1);
        for(int i = 0;i < A.size()+1;i++)
        {
            f[i].resize(m+1);
        }
        for(int i = 0;i < A.size()+1;i++)
        {
            f[i][0] = 0;
        }
        for(int i = 0;i < m+1;i++)
        {
            f[0][i] = 0;
        }
        for(int i = 1;i < A.size()+1;i++)
        {
            for(int v = 1;v < m+1;v++)
            {
                if(v < A[i-1])
                   f[i][v] = f[i-1][v];
                else
                   f[i][v] = max(f[i-1][v],f[i-1][v-A[i-1]]+A[i-1]);
            }
        }
        return f[A.size()][m];
    }
};

 

LintCode 125.BackpackII

Description

Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?

Example

Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. The maximum value is 9.

Challenge

O(n x m) memory is acceptable, can you do it in O(m) memory?

这道题目是上一题的升级版,加入了每个物品的不同价值,但实际上两题没有任何区别_(¦3」∠)_,这里先给出此题的状态转移方程:

只是把上一题max中后一项加的A[i-1]换成了V[i-1],状态转移图这里也不再画了,这里讨论如何利用O(m)空间实现:

可以注意到在这两个题目中,当前计算的行之和上一行有关,这就有了优化的空间。然而当我们尝试只用一行去记录dp值时,会发现如果按照从左到右的顺序计算,前面计算的值会把后面的覆盖掉,例如在上一题的例子里,如果我们先更新了dp[1][4]为3,在更新dp[1][7]的时候,正确的值应为2+3(dp[1][4]+A[1]),但因为此时dp是一维向量,得到的值为3+3。对此解决方案是从右到左倒着更新

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        // write your code here
        vector<int> f;f.resize(m+1);
        for(int i = 0;i < A.size();i++)
        {
            for(int v = m;v >= A[i];v--)
            {
                f[v] = max(f[v],f[v-A[i]]+V[i]);
            }
        }
        return f[m];
        
    }
private:
    int max(int a,int b)
    {
        return (a>b)?a:b;
    }

};

LintCode 669.Coin Change

Description

:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

Example

Given coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

Given coins = [2], amount = 3
return -1.

最少硬币置换问题。设F[i]为置换面值i时最少的硬币数,这里的更新公式为:

(我真的是醉了,图片怎么都上传不上去,下次更新我再试试微笑,6.9注:今天还是生病状态,拖到下次再弄这里吧......)

注意我们应该讲F[0]设置为0,其余初始值设置为-2(或者某个标记数),让循环能够正常开始,以Example为例,F值的更新如下图所示:

0 1 2 3 4 5 6 7 8 9 10 11
1 0 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

5 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

0 1 2 3 4 5 6 7 8 9 10 11
1 0 1 2 3 4 5 6 7 8 9 10 11
2 0 1 1 2 2 3 3 4 4 5 5 6

5 0 1 1 2 2 1 2 2 3 3 2 3

此题的testcase里会出现硬币面值为0的情况,这点需要主要。

class Solution {
public:
    /**
     * @param coins: a list of integer
     * @param amount: a total amount of money amount
     * @return: the fewest number of coins that you need to make up
     */
    int coinChange(vector<int> &coins, int amount) {
        // write your code here
        vector<int> f(amount + 1, -2); f[0] = 0;
sort(coins.begin(), coins.end());
coins.erase(remove(coins.begin(), coins.end(), 0), coins.end());
for (int i = 0; i < coins.size(); i++)
{
for (int v = 0; v < amount + 1; v++)
{
if (v >= coins[i])
{
if (f[v - coins[i]] == -2)
{
if (f[v] == -2)
f[v] = -2;
else
;
}
else
{
if (f[v] == -2)
f[v] = f[v - coins[i]] + 1;
else
f[v] = min(f[v], f[v - coins[i]] + 1);
}
}
}
}
return (f[amount] == -2) ? -1 : f[amount];
    }

};

LeetCode 72Edit Distance

Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.

You have the following 3 operations permitted on a word:

  1. Insert a character
  2. Delete a character
  3. Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation: 
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:

Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation: 
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

这个题目我没有想到是DP(真的很不应该),完了一看别人的解答:This is a classic problem of Dynamic Programming微笑。去你的小jerry......还是自己太菜了。

这里我们使用的状态值是dp[i][j],表示将word1[0...i-1]变化为word2[0......j-1]所需要的最少的操作数。首先我们可以确定边界值:dp[i][0] = i,dp[0][j]=j

接下来推导更新公式:

如果word[i-1] == word[j-1],那么dp[i][j] = dp[i-1][j-1],这是最简单的情况;

如果不相等,则要考虑如下三种情况:

1.通过替换word1[i-1]为word2[j-1],使得word1[0......i-1]变化为word2[0......j],这时的dp[i][j] = dp[i-1][j-1] +1;

2.首先我们通过dp[i-1][j]的代价把word1[0......i-1]中的word1[0......i-2]变为word2[0......j-1],然后再删掉word1[i-1]就可以了,这时dp[i][j] = dp[i-1][j] + 1;

3.首先通过dp[i][j-1]的代价把word1[0....i-1]变换成word2[0......j-2],再加上word2[j-1]即可,这时dp[i][j] = dp[i][j-1] + 1;

那么很明显,更新公式为

dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1) word1[i-1] != word2[j-1]

class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size(); int m = word2.size();
vector<vector <int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++)
dp[i][0] = i;
for (int j = 1; j <= m; j++)
dp[0][j] = j;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (word1[i - 1] == word2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1);
}
}
return dp[n][m];
}
private:
int min(int a, int b, int c)
{
int res;
if (a < b)
res = a;
else
res = b;
if (res < c)
return res;
else
res = c;
return res;
}
};

猜你喜欢

转载自blog.csdn.net/qq_21602549/article/details/80497683
今日推荐