11.2最大连续子序列和
给定一个数字序列A1,A2,...,An,求i,j(1<=i<=j<=n),使得Ai+..+Aj最大,输出这个最大和
样例:
-2 11 -4 13 -5 -2
显然11+(-4)+13=20为和最大的选取情况
暴力:O(n^2) dp:O(n)
A[i]记录序列
记dp[i]为以A[i]结尾的连续序列(将问题划分为子问题,序列划分为子序列,极限自然是长度为1的序列,可以理解为
以A[i]结尾)的最大和,则dp[i]有两种状态(关键)
1.前面最大和为负数或者i==0,此时dp[i]=A[i] 序列长度为1
2.dp[i]=dp[i-1]+A[i] 序列长度不为1边界:dp[0]=A[0]
状态转移方程: dp[i]=max(dp[i-1]+A[i],A[i]);
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010;
int A[N],dp[N];
int main(){
// freopen("input.txt","r",stdin);
int n;
while(cin>>n){
for(int i=0;i<n;i++){
cin>>A[i];
}
dp[0]=A[0];
for(int i=1;i<n;i++){
dp[i]=max(A[i],dp[i-1]+A[i]);//直接自己(之前最大为负) 或者+上之前最大
}
cout<<*max_element(dp,dp+n)<<endl;
}
return 0;
}
11.3 最长不降子序列(LIS)
在一个数字序列中,找到一个最长的子序列(可以不连续),使得这子序列是不降(非递减)的。
eg:A={1,2,3,-1,-2,7,9}
最长不降子序列:1 2 3 7 9 长度为5
在一个数字序列中,找到一个最长的子序列(可以不连续),使得这子序列是不降(非递减)的。
eg:A={1,2,3,-1,-2,7,9}
最长不降子序列:1 2 3 7 9 长度为5dp[i]是以A[i]结尾的最长不降子序列长度
1.前面有比A[i]小的(或等于)A[j] 则 dp[i]=dp[j]+1 (dp[j]是所有小于j<i且A[j]<=A[i]中的最大值)
2.前面没有比A[i]小的(或等于)A[j] 则dp[i]=1 A[i]自己一个人组成一个序列dp[i]=max(1,dp[j]+1)
(j=1,2,...i-1,A[j]<A[i])
#include<iostream>
using namespace std;
const int N=100;
int A[N],dp[N];
int main(){
// freopen("input.txt","r",stdin);
int n;
while(cin>>n){
for(int i=0;i<n;i++){
cin>>A[i];
}
int ans=0;
for(int i=0;i<n;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(A[j]<=A[i]&&dp[j]+1>dp[i]){
dp[i]=dp[j]+1;
}
}
ans=max(ans,dp[i]);//更新最大长度
}
cout<<ans<<endl;
}
return 0;
}
11.4最长公共子序列(LCS)
给定两个字符串(或数字序列)A和B,求一个字符串,使得这个字符串是A和B的最长公共部分(子序列可以不连续)
A[i] B[i] 存字符串A,B,由于dp[0][j]=dp[i][0]=0 长度为0时的边界 下标都统一从1开始
dp[i][j]表示以A[i]、B[j]结尾的两个子串的最长公共子序列长度
则:
dp[i][j]={
dp[i-1][j-1],A[i]==B[j]
max(dp[i-1][j],dp[i][j-1]),A[i]!=B[j]
}注意:对于 dp[i-1][j-1],A[i]==B[j] ,即第一个分支,一定是dp[i-1][j-1]两个下标都要减,不能写成dp[i][j-1]或者dp[i-1][j]否则可能出错 eg:abc acc i=j=3时 dp[2][2]+1=2 但是dp[3][2]+1=3 重复利用了A[3]导致出错
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1000;
char A[N],B[N];
int dp[N][N];
int main(){
// freopen("input4.txt","r",stdin);
gets(A+1);//下标从1开始
gets(B+1);
int lenA=strlen(A+1);//从A[1]开始计算长度
int lenB=strlen(B+1);//从A[1]开始计算长度
for(int i=1;i<=lenA;i++){//要加等号 0 1 2 3 长度3 A[1]~A[3]
dp[i][0]=0;
}
for(int j=1;j<=lenB;j++){
dp[0][j]=0;
}
//或者干脆整个数组初始化为0
//memset(dp,0,sizeof(dp));
for(int i=1;i<=lenA;i++){
for(int j=1;j<=lenB;j++){
if(A[i]==B[j]){//A[i]与B[j]比 不要写成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[lenA][lenB]<<endl;
return 0;
}
11.5最长回文子串
给出一个字符串S,求S的最长回文子串的长度
样例:
字符串"PATZJUJZTACCBCC"最长回文子串为"ATZJUJZTA",长度为9
dp[i][j]表示A[i~j]子串是否是回文串,是1 否0
dp[i][j]={
dp[i-1][j-1],A[i]==A[j]
0,A[i]!=A[j]
}边界:dp[i][i]=1 dp[i][i+1]=(A[i]==A[i+1)?1:0
则枚举时长度L可以从3开始
枚举是以子串长度和左边坐标i(初始位置进行的)
边界计算除了所有长度为1和2的子串情况
第1遍动规:计算所有长度为L=3的所有子串情况
第2遍动规:计算所有长度为L=4的所有子串情况
...
第len-2遍动规:计算所有长度为L=len的所有子串情况 //其实就是判断整个字符串是否也是之所以要这么复杂是因为递归表达式的特殊,计算下一个状态dp[i][j]需要利用dp[i-1][j-1]这个长度比其小2的子串
L=3之前0要知道
L=4之前2要知道
...
L=len之前len=len-2要知道
#include<iostream>
#include<cstring>
using namespace std;
const int N=1000;
char S[N];
int dp[N][N];
int main(){
freopen("input5.txt","r",stdin);
int len,ans;
int s,e;//辅助 自己加的
while(gets(S)!=NULL){
len=strlen(S);
ans=1;//最大回文子串长度 初值为1
memset(dp,0,sizeof(dp));
//边界
for(int i=0;i<len;i++){
dp[i][i]=1;
if(S[i]==S[i+1]){
dp[i][i+1]=1;
ans=2;
}//否则默认0 dp[i][i+1]不回文
}
//动规
for(int L=3;L<=len;L++){
for(int i=0;i+L-1<len;i++){//右边界小于len
int j=i+L-1;
if(S[i]==S[j]){
dp[i][j]=dp[i+1][j-1];
ans=L;//更新最长回文子串长度
s=i,e=j;
}//否则不是回文串 默认值0表false
}
}
cout<<ans<<endl;
S[e+1]='\0';cout<<S+s<<endl;
}
return 0;
}