dp入门 最长公共序列,最长上升序列

今天是看kuangbin专题12 的第一题hdu的1024最大n对子序列的最大和看不懂,就来看基础的dp最长上升序列

1.最长公共序列

//date:2020.4.18

//author:lujunfeng
/*

状态转移方程
dp[i][j]=0(i==0||j==0)
=dp[i-1][j-1](i!=0&&j!=0&&s1[i]==s2[j])
=max(dp[i-1][j],dp[i][j-1]) (s1[i]!=s2[j])

理解:在涉及i和j判断时,不相等,就是i-1或者j-1时的最大数,相等,在i-1和j-1的时候+1

初始状态全为0,一个小细节是存储在数组中的时候是从0开始的,所以应该判断的是i-1和j-1的下标的数组,当前的字符是否相等
*/
#include <iostream>

using namespace std;
int dp[10][10];
int main()
{
char s2[8]="BDCABA";
char s1[8]="ABCBDAB";
for(int i=0; i<=7; i++)
for(int j=0; j<=6; j++)
{
if(i==0||j==0)
dp[i][j]=0;
else
{
if(s1[i-1]==s2[j-1])//因为数组是按照0开始存起的,所以需要注意,千万要注意
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
}
for(int i=0; i<=7; i++)
{
for(int j=0; j<=6; j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}
cout<<dp[7][6]<<endl;
return 0;
}
改进,用滚动数组处理,因为每次只是一行一行地处理,而且只与上一行有关,所以可以用一位数组,当然了,各有利弊
#include <iostream>
//记忆化数组,因为每一行只与上一行有关,当然了用二维数组更好理解,但是用一维数组
//更省空间,自己举例子实现一遍就能更加理解滚动数组的过程
using namespace std;
int dp[10];
int main()
{
char s2[8]="BDCABA";
char s1[8]="ABCBDA";
for(int i=0; i<=6; i++)
for(int j=0; j<=6; j++)
{
if(i==0||j==0)
dp[j]=0;
else
{
if(s1[i-1]==s2[j-1])//因为数组是按照0开始存起的,所以需要注意
dp[j]=dp[j-1]+1;
else
dp[j]=max(dp[j-1],dp[j]);
}
}

cout<<dp[6]<<endl;
return 0;
}
/*
2.最长上升子序列,动态规划处理

//dp(j) = { max(dp(i)) + 1, i<j且L[i]<L[j] },这个状态方程在处理的时候,还是两重循环,复杂度依然是n^n

#include<bits/stdc++.h>
using namespace std;
int main()
{
int L[7]= {5,6,7,1,2,3};
int dp[7]= {1,1,1,1,1,1};
cout<<"趟";
for(int p=0; p<6; p++)
cout<<p<<" ";
cout<<endl;
for(int j=0; j<6; j++)
{
cout<<j<<" ";
for(int i=0; i<j; i++)
if(L[i]<L[j]&&dp[j]<dp[i]+1)
dp[j]=dp[i]+1;
for(int k=0; k<6; k++)
cout<<dp[k]<<" ";
cout<<endl;
}
cout<<dp[5]<<endl;
return 0;
}

输出的结果
趟 0 1 2 3 4 5
0  1 1 1 1 1 1
1  1 2 1 1 1 1
2  1 2 3 1 1 1
3  1 2 3 1 1 1
4  1 2 3 1 2 1
5  1 2 3 1 2 3
3

贪心+二分法解决最长上升序列的问题,但是此方法的局限在于,最后只能找出最长上升子序列的长度,因为只能最后的结果表示最大可能,长度相同在于,始终在做替换操作,并没有进行移动操作,此方法的复杂度为nlogn

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn =300003, INF = 0x7f7f7f7f;
int low[maxn];

int n, ans;

int binary_search(int *a, int R, int x)
//二分查找,返回a数组中第一个>=x的位置
{
int L = 1, mid;
while(L <= R)
{
mid = (L+R) >>1;
if(a[mid] <= x)
L = mid + 1;
else
R = mid - 1;
}
return L;
}

int main()
{
int a[9]= {0,3,1,2,6,4,5,10,7};
n=8;
low[1] = a[1];
ans = 1; //初始时LIS长度为1
for(int i=2; i<=n; i++)
{
if(a[i] > low[ans]) //若a[i]>=low[ans],直接把a[i]接到后面
low[++ans] = a[i];
else //否则,找到low中第一个>=a[i]的位置low[j],用a[i]更新low[j]
low[binary_search(low, ans, a[i])] = a[i];
}
printf("%d\n", ans); //输出答案
for(int i=1;i<=ans;i++)
cout<<low[i];
return 0;
}

猜你喜欢

转载自www.cnblogs.com/someonezero/p/12728964.html
今日推荐