898 数字三角形
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=510,INF=1e9;
int f[N][N],a[N][N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=0;j<=i+1;j++)
f[i][j]=-INF;
f[1][1]=a[1][1];
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{ f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
//cout<<f[i][j]<endl;
}
int res=-INF;
for(int i=1;i<=n;i++) res=max(res,f[n][i]);
cout<<res<<endl;
return 0;
}
895最长上升子序列
DP问题的考虑是从维数小开始考虑。
f[i]所有以第i个数结尾的上升子序列。
以i-1是啥来进行分类。f[i]=f[i-1]+1;
时间复杂度分析:状态的数量为n,计算的数量也是n,所以时间的复杂度为n2;
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n,a[N],f[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
f[i]=1;//只有a[i]一个数
for(int j=1;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]);
printf("%d",res);
return 0;
}
求方案,就是把转移的过程记录下来,然后进行输出。
897 最长公共子序列
1、状态表示:
- 集合:f[i,j]第一个序列的前i个字母以及,第二个序列的第j个字母的最长公共子序列
- 属性:最大
2、状态计算:a[i],b[j]是否在集合当中。一共有四种情况,不重不漏。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;
int f[N][N];
char a[N],b[N];
int main(){
scanf("%d%d",&n,&m);
scanf("%s%s",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);
}
printf("%d\n",f[n][m]);
return 0;
}
282 石子合并
设有N堆石子排成一排,其编号为1,2,3,…,N。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
区间DP问题
1、状态表示
- 集合:所有将i堆石子到第j堆石子合并成一堆石子的合并方式。
- 属性:min
2、状态计算:以最后一次分界线的位置进行分类,分成多类。
f[i,j]=min{f[i,k]+f[k+1,j]+s[j]-s[i-1]};
#include<iostream>
#include<algorithm>
using namespace std;
const int N=310;
int n;
int s[N];
int f[N][N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=1;i<=n;i++) s[i]+=s[i-1];
for(int len=2;len<=n;len++)
for(int i=1;i+len-1<=n;i++)//起点
{
int l=i,r=i+len-1;
f[l][r]=1e8;
for(int k=l;k<r;k++)
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
printf("%d",f[1][n]);
return 0;
}