LIS:最长上升子序列问题
边界处理: f[0]=0;
方法一:考虑f[i]可以推导出哪些状态
for(int i = 0; i < n; i++) i:结尾下标
for(int j = i + 1; j <= n; j++) 下一个数
if(a[j] > a[i]) f[j]=max(f[j],f[i]+1);
方法二:考虑f[i]可以由哪些状态得到
此方法较为常用
for(int i = 1; i <= n; i++)
for(int j = 0;j < i;j++) 前一个数
if(a[j] < a[i]) f[i]=max(f[i],f[j]+1);
目标
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
求最优解的方案是哪个
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
if(a[j]>a[i])
if(f[j]+1>f[i]){
f[i]=f[j]+1;
pos[i]=j;//记录由某点推导过来的该点的下标
}
}
}
void print(int i)
{
if(i==0) return ;
print(pos[i]);//递归处理
cout<<a[i]<<" ";
}
int ans=0,tail;
for(int i=1;i<=n;i++)
if(f[i]>ans) ans=f[i],tail=i;
cout<<ans<<endl;
print(tail);
二分优化
q [ i ] q[i] q[i]表示长度为i的上升子序列的结尾的那个数,枚举序列中的数 a [ i ] a[i] a[i],将其放到 q q q数组中最大的小于 a [ i ] a[i] a[i]的数后,通过二分实现。时间复杂度 n l o g n nlogn nlogn.
for(int i=1;i<=n;i++) cin>>a[i];
int len=0;
for(int i=1;i<=n;i++)
{
int l=0,r=len;
while(l<r)
{
int mid=(l+r+1)>>1;
if(q[mid]<a[i]) l=mid;
else r=mid-1;
}
len=max(len,r+1);
q[r+1]=a[i];
}
cout<<len;
LCS:最长公共子序列问题
边界处理:
for(int i = 0; i <= n; i++) f[i][0]=0;
for(int j = 0; j <= m; j++) f[0][j]=0;
状态转移
for(int i = 1;i <= n;i++)
for(int j=1; j<= m ;j++)
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(a[i] == a[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
目标
cout<<f[n][m]<<endl;
数字三角形模型
思路
当前坐标(i,j) 可以走到(i+1,j) (i+1,j+1)
边界处理
f[1][1]=a[1][1]
状态转移
for(int i = 1;i<n;i++)
for(int j=1;j<=i;j++){
f[i+1][j]=max(f[i+1][j],f[i][j]+a[i+1][j]);
f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+a[i+1][j+1]);
}
目标结果
int ans=0;
for(int j=1;j<=n;j++)
ans=max(ans,f[n][j]);