leetcode中一些经典动态规划题(不定期更新)

37 篇文章 0 订阅

115. Distinct Subsequences
115
状态转移方程:
d p [ i ] [ j ] = { d p [ i ] [ j − 1 ] , s [ j ] ! = t [ i ] d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j − 1 ] , s [ j ] = = t [ i ] dp[i][j]=\begin{cases} dp[i][j - 1],\qquad\qquad\qquad\qquad\quad s[j] !=t[i] \\\\ dp[i][j - 1] + dp[i - 1][j - 1],\quad s[j] ==t[i] \end{cases} dp[i][j]=dp[i][j1],s[j]!=t[i]dp[i][j1]+dp[i1][j1],s[j]==t[i]
理解:
dp[i][j]表示s[0:j]和t[0:i]的结果
当s[j] 与 t[i]不相等时,当前结果即为前一轮结果
当两者相等时,当前结果可以分为两个情况:
s[j] 与 t[i] 匹配,此时对应的结果为dp[i - 1][j - 1]
s[j] 不与 t[i] 匹配,此时的结果为dp[i][j - 1]
因此最终的结果为两者的和
举个例子:
先补一行一列,第0行表示当t为空时,都可以匹配,第0列表示当s为空时无法匹配

   0  r b b b t (s)
0  1  1 1 1 1 1
r  0  1 1 1 1 1
b  0  0 1 2 3 3
t  0  0 0 0 0 3
(t)

s = rbbbt,t=rbt,当遍历到t[i] = ‘b’ s[j]=‘b’(第二个)时,此时有两个选择:t[i]与s[j]匹配,即

rbb
^ ^

此时的dp[i][j] = dp[i - 1][j - 1]
或者t[i]不与s[j]匹配,即

rbb
^^

此时的dp[i][j] = dp[i][j - 1]
最后的代码:

class Solution {
public:
    int numDistinct(string s, string t) {
        int lenS = s.length(), lenT = t.length();
        vector<vector<long long>> dp(lenT + 1, vector<long long>(lenS + 1, 0));
        for(int i = 0; i <= lenS; ++i)
            dp[0][i] = 1;
        for(int i = 1; i <= lenT; ++i)
            dp[i][0] = 0;
        for(int i = 1; i <= lenT; ++i)
            for(int j = 1; j <= lenS; ++j)
            {
                if(s[j - 1] == t[i - 1]) dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1];
                else dp[i][j] = dp[i][j - 1];
            }
        return dp[lenT][lenS];
    }
};

940. Distinct Subsequences II
940
状态转移方程:
d p [ i ] = { d p [ i ] + d p [ j ] , s [ j ] ! = s [ i ] d o n o t h i n g , s [ j ] = = t [ i ] dp[i]=\begin{cases} dp[i] + dp[j],\quad s[j] !=s[i] \\\\ do \quad nothing,\quad s[j] ==t[i] \end{cases} dp[i]=dp[i]+dp[j],s[j]!=s[i]donothing,s[j]==t[i]
j的取值:[0, i)
理由:
dp[i]表示以s[i]结尾的子序列的个数
举例说明:

abca:
a: a
b: ab, b
c: ac, abc, bc, c
a: aa, aba, ba, aca, abca, bca, ca, a(相当于第一个a没做)
const int MOD = 1e9 + 7;
class Solution {
public:
    int distinctSubseqII(string S) {
        int result = 0;
        vector<int> dp(S.length(), 1);
        for(unsigned i = 0; i < S.length(); ++i)
        {
            for(unsigned j = 0; j < i; ++j)
            {
                if(S[j] != S[i])
                {
                    dp[i] += dp[j];
                    dp[i] %= MOD;
                }
            }
            result += dp[i];
            result %= MOD;
        }
        return result;
    }
};

474. Ones and Zeroes
474
给定一个字符串数组和两个整数m,n,其中m表示0能出现的次数上限,n表示1能出现的次数上限,给出最多数量的字符串,使其1和0的个数都满足要求
典型的二维费用背包问题,先创建一个二维数组res, res[i][j]表示只使用i个0和j个1,能得到的最大价值,每次遇到一个字符串,先分别算出0和1的数目,作为二维费用中各自的cost,且每个元素的value均为1
因此 r e s [ i ] [ j ] = m a x ( r e s [ i ] [ j ] , r e s [ i − z e r o i ] [ j − o n e i ] + 1 ) res[i][j] = max(res[i][j], res[i - zero_i][j - one_i]+1) res[i][j]=max(res[i][j],res[izeroi][jonei]+1),最后返回res[m][n]即可

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {

		int len = strs.size();
		vector<vector<int>> res(m + 1, vector<int>(n + 1, 0));
		//int maxn = 0, pos = 1;
		for (string s : strs)
		{
			int zero = 0, one = 0;
			for (char c : s)
				if (c == '0') zero++;
				else one++;
			for(int i = m; i >= zero; --i)
				for (int j = n; j >= one; --j)
				{
					res[i][j] = max(res[i][j], res[i - zero][j - one] + 1);
					//maxn = max(res[i][j], maxn);
				}
			//pos++;
		}
		return res[m][n];
	}
};

322. Coin Change
322
给定一个整型数组coins和一个整数amount,其中coins中的数可以用无限次,判断最少需要使用多少个coins中的数,使其之和为amount,若不存在,则返回-1。
状态转移方程:
d p [ i ] = m i n ( d p [ i ] , d p [ i − c ] + 1 ) dp[i]=min(dp[i], dp[i-c]+1) dp[i]=min(dp[i],dp[ic]+1)
其 中 c 为 c o i n s 中 的 数 其中c为coins中的数 ccoins
比如例题中, d p [ 11 ] = m i n ( d p [ 11 ] , d p [ 11 − 1 ] + 1 , d p [ 11 − 2 ] + 1 , d p [ 11 − 5 ] + 1 ) dp[11]=min(dp[11], dp[11-1]+1, dp[11-2]+1, dp[11-5]+1) dp[11]=min(dp[11],dp[111]+1,dp[112]+1,dp[115]+1)

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, 1e9);
        sort(coins.begin(), coins.end());
        dp[0] = 0;
        for(int i = 1; i <= amount; ++i)
        {
            int minn = 1e9;
            for(int c: coins)
            {
                if(i - c < 0) break;
                minn = min(minn, dp[i - c]);
            }
            dp[i] = minn + 1;
        }
        if(dp[amount] >= 1e9) return -1;
        else return dp[amount];
    }
};

416. Partition Equal Subset Sum
416
给定一个数组,判断其是否能被分成两个和相等的子数组
状态转移方程:
d p [ j ] = d p [ j ] ∣ ∣ d p [ j − n u m s [ i ] ] dp[j] =dp[j] || dp[j - nums[i]] dp[j]=dp[j]dp[jnums[i]]
举个例子:[2, 2, 3, 4, 5]
初始化dp[0]=true
第一轮dp[2] = dp[2] || dp[2-2] = true
第二轮dp[4] = dp[4] || dp[4-2] = true
即后一项对应不使用该元素时的和
最后只要返回原数组总和sum 的 1/2即可
完整代码:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int len = nums.size();
        if(len < 2) return false;
        long long int sum = 0;
        for(int& i: nums) sum += i;
        if(sum % 2) return false; // sum为奇数,则不可划分
        sum /= 2;
        vector<bool> dp(sum + 1, 0);
        dp[0] = true; // 初始化dp[0] = true
        for(int i = 0; i < len; ++i)
        {
            for(int j = sum; j >= nums[i]; --j)
                dp[j] = dp[j] || dp[j - nums[i]];
        }
        return dp[sum];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值