"那坚守,某一刻化作乌有"
我们可以看出,s1,s2拼接后的字符串s3长度一定是一样的。并且s3中的子串一定是s1或s2当中的子串,因此要看s1、s2能否拼接成s3本质就是查找公共子序串。
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];
}
};
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;
}
};
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;
}
};
背包问题是一个经典的dp题型。
#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;
}
可以看出,背包问题的这两个条件,在状态转移方程上几乎是相似的,只是加上了一些限制条件。
#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;
}
我们换一个思路,该题目就是要让分成两个值相同的数组,也就意味着每一个数组的值都是原数组sum总和的一半。当sum / 2 商为奇数是,根本可能划分成两个值相等的数组。 因此,我们只需要求半边数组,求它的和为 sum / 2 即可。
同样,我们遍历数组选数时,就是一种选与不选的"01背包问题",该题解的本质又是一种简单的背包模型。从nums的数组中,选取n个数,能让他的最终值 == sum / 2,不过此时没有所谓的背包容量的限制。
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];
}
};
本篇到此结束,感谢你的阅读。
祝你好运,向阳而生~