1. 数字三角形
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从上到下:
#include<bits/stdc++.h>
using namespace std;
const int N=510,INF = 1e9;
/**
DP
*/
int w[N][N],f[N][N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++) cin>>w[i][j];
}
for(int i = 0;i<=n;i++){
for(int j=0;j<=i+1;j++) f[i][j] = -INF;
}
f[1][1] = w[1][1];
for(int i=2;i<=n;i++){//层数
for(int j=1;j<=i;j++){
f[i][j]=max(f[i-1][j-1]+w[i][j],f[i-1][j]+w[i][j]); //左上方 +w[i][j] , 右上方 + w[i][j]
}
}
int res=-INF;
for(int i=1;i<=n;i++) res=max(res,f[n][i]);
cout<<res;
return 0;
}
从下到上:
#include<bits/stdc++.h>
using namespace std;
const int N=510;
/**
DP
*/
int w[N][N],f[N][N];//f[i][j]表示所有路线的最大值, 从底向上
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++) cin>>w[i][j];
}
for(int i=0;i<n;i++)f[n][i] = w[n][i];//初始化 最下一层f[n][i]最大值即为w[n][i]
for(int i=n-1;i>=0;i--){
for(int j=0;j<=i;j++){
f[i][j]=max(f[i+1][j]+w[i][j],f[i+1][j+1]+w[i][j]); //左下方 +w[i][j] , 右下方 + w[i][j]
}
}
cout<<f[0][0];
return 0;
}
2.最长上升子序列 O( n 2 n^2 n2)
给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少
/*
状态表示 f[i] : 集合:所有以第i个数结尾的上升子序列
属性:Max
状态计算 以倒数第二个数作为划分依据 0、1、2、···、i-1
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,a[N],f[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
f[i] = 1;
for(int j=0;j<i;j++){
if(a[j]<a[i]) f[i] = max(f[i] , f[j] + 1);
}
}
int res=0;
for(int i =1;i<=n;i++) res = max(res,f[i]);
cout<<res;
}
扩展:求最长上升子序列是哪一个
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,a[N],f[N],g[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
f[i] = 1;
for(int j=0;j<i;j++){
if(a[j]<a[i]){
if(f[i]<f[j]+ 1){
f[i] = f[j]+ 1;
g[i] = j;
}
}
}
}
int k = 1;
for(int i=1;i<=n;i++){
if(f[k]<f[i]) {
k = i;
}
}
cout<<f[k];//输出最长上升子序列长度
//输出最长上升子序列
for(int i =1 , len = f[k] ;i<=len;i++){
cout<<a[k]<<" ";
k = g[k];
}
}
3.最长上升子序列 II
4.最长公共子序列
给定两个长度分别为N和M的字符串A和B,求既是A的子序列又是B的子序列的字符串长度最长是多少
按照a[i]
和b[j]
是否包含在该子序列中进行划分,如上图,四种情况,第一种不选a[i]
不选b[j]
的情况为f[i-1][j-1]
,最后一种选a[i]
也选b[j]
这种情况为f[i-1][j-1]+1
(此时a[i]
=b[j]
) ;对于第二种情况和第三种情况,f[i-1][j]
表示在第一个序列前i-1
个字母中出现,第二个序列前j
个字母中出现的子序列,这种情况一定包含不选a[i]
选b[j]
的情况,且f[i-1][j]
一定属于f[i][j]
,最终求的是f[i][j]
的最大值,那么就可以用f[i-1][j]
代替不选a[i]
选择b[j]
这种情况,同理也可以用f[i][j-1]
代替选择a[i]
不选b[j]
这种情况。同时对于f[i-1][j-1]
这种情况根据上述分析也一定被 f[i-1][j]
和f[i][j-1]
包括
综上:f[i][j] = Max(f[i-1][j], f[i][j-1]) 如果a[i] =b[j]那么再取 f[i][j] = Max(f[i-1][j], f[i][j-1] ,f[i-1][j-1] + 1)
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n , m;
char a[N],b[N];
int f[N][N];
int main(){
cin >> n >> m;
cin >> a+1 >> b+1;
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] == b[j]) f[i][j] = max(f[i][j] , f[i-1][j-1] + 1);
}
}
cout << f[n][m];
return 0;
}