在做动态规划的相关题,我们会遇到最长公共子序列,最长递增子序列,最长递增公共子序列等相关类型题,那么本期内容将围绕其展开讨论。
我们都知道动态规划的思想可以求解极值问题,通过对于其数组内参数不断的递推结合根据其dp数组的含义所推出的状态转移方程得到所求解,那么咱们根据动态规划如何求解子序列问题呢?
首先给出例题:
首先给出X = {1,2,3,6,2,3},Y = {1,2,6,3,1,2} 这两个序列,请你求得X和Y的一个最长公共子序列的长度。
首先分析这道题的时候咱们要注意这是一个线性DP问题,这道题可以暴力法枚举所有X的子序列并验证其是否是Y的子序列,很显然其复杂度会O(2^(m + n)),而利用动态规划对这两个序列进行遍历,就是两个for循环的嵌套,其复杂度为O(nm)。
咱们先研究dp数组的含义,这道题我们不妨设dp[i][j]数组,其含义为遍历到X对应的前i个元素与Y对应的前j个元素的最长公共子序列的长度。
那么假设0代表不取,1代表取,那么其集合就可以表示为:01,10,11
如果Xi = Yj(11) ,那么可以推出状态转移方程为:dp[i][j] = dp[i - 1][j - 1] + 1;
如果Xi ≠ Yi(01或10),那么:dp[i][j] = max(dp[i - 1][j] , dp[i][j - 1]);
#include
using namespace std;
const int N = 1000;
int a[N],b[N];
int n;
int dp[N][N];
int main()
{
cin >> n ;
for(int i = 1;i <= n;i++)
{
cin >> a[i] >> b[i];
}
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(a[i] == b[j])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else{
dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
}
}
}
cout << dp[n][n];
return 0;
}
给定一个长度为n的数组a[]= {5,6,7,4,2,8,3},请你求出他的最长递增子序列的长度。
在处理这道题的时候我们应该思考如何保持让其记录并保证递增?
首先dp[i]数组的含义为前i个元素的最长递增子序列的长度,我们不妨设一个k来遍历前i个元素
如果a[k] < a[i],那么更新dp[i]的最大值:dp[i] = max(dp[i],dp[k] + 1);
#include
using namespace std;
const int N = 1000;
int a[N];
int dp[N];
int main()
{
int imax = 1;
for(int i = 0;i <= 9;i++) cin >> a[i];
for(int i = 0;i <= 9;i++)
{
dp[i] = 1;
for(int k = 0;k <= i;k++) //筛选当前i值的递增子序列的最大值
{
if(a[i] > a[k]) //若k < i,则满足递增条件
{
dp[i] = max(dp[i],dp[k] + 1); //dp[k]存放的是前k个元素筛选的最大值,+1加的是a[i]这个成立的情况,不断筛选使得最后筛选出的dp[i]值最大
}
}
imax = max(dp[i],imax);//筛选出每个前i个元素的最大数中的最大值
}
cout << imax;
return 0;
}
在讲述了前两个后我们可以把上面两种求解方式所结合在一起。
首先定义dp[i][j]数组表示含义为遍历到X对应的前i个元素与Y对应的前j个元素的最长公共递增子序列的长度.
我们将其分成选a[i]与不选a[i]:
如果不选a[i],那么其dp[i][j] = dp[i - 1][j];
如果要选择a[i],那么如果a[i] == b[j],分析如何使其递增,根据上面所讲到的最长递增子序列,设置第三方参数k来遍历b数组内前j个元素内的子序列判断是否存在递增序列,如果存在则与前面所筛选的最大值来比较得出在这种情况的最值。
#include
using namespace std;
const int N = 1000;
int a[N],b[N];
int dp[N][N];
int main()
{
for(int i = 0;i <= 6;i++)
{
cin >> a[i] >> b[i];
}
for(int i = 0;i <= 6;i++)
{
for(int j = 0;j <= 6;j++)
{
if(a[i] == b[j]) //选择a[i]的集合
{
int maxv = 1;
for(int k = 0;k <= j;k++) //遍历前j个元素的递增情况
{
if(b[k] < b[j]) //如果k < j,则满足情况
maxv = max(maxv , dp[i - 1][k] + 1);筛选当前j值的最大值,dp[i - 1][k]是代表b数组的前k个元素的最大值,最后筛选出选a[i]情况下的前j个元素的最值来存入maxv中
}
dp[i][j] = max(dp[i - 1][j],maxv); //比较选与不选a[i]
}
}
}
int res = 0;
for(int i = 0;i <= 6;i++) res = max(res , dp[6][i]);//比较i = 6时候的每个j值所对应的dp数组的max
cout << res;
return 0;
}
好了,今天的内容就到这里了,感谢收看,记得三连支持,后序会继续更新算法的相关知识。