模板题链接:
线性规划
数字三角形
思路:
状态表示: a r r [ i ] [ j ] arr[i][j] arr[i][j]表示从(1,1)出发到(i,j)的最长路径。
状态表示: a r r [ i ] [ j ] = m a x ( a r r [ i − 1 ] [ j − 1 ] , a r r [ i − 1 ] [ j ] ) + a r r [ i ] [ j ] arr[i][j]=max(arr[i-1][j-1],arr[i-1][j])+arr[i][j] arr[i][j]=max(arr[i−1][j−1],arr[i−1][j])+arr[i][j]
代码:
#include<iostream>
using namespace std;
const int N = 505,INF=1e9;
int arr[N][N];
int main(){
int n;
cin>>n;
for(int i=0;i<=n;i++){
//初始化
for(int j=0;j<=n;j++){
arr[i][j]=-INF;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>arr[i][j];
}
}
for(int i=2;i<=n;i++){
//状态转移
for(int j=1;j<=i;j++){
arr[i][j]+=max(arr[i-1][j-1],arr[i-1][j]);
}
}
int res=-INF;
for(int i=1;i<=n;i++) res=max(res,arr[n][i]);
cout<<res<<endl;
return 0;
}
最长上升子序列:
思路:
状态表示: f [ i ] f[i] f[i]表示以i号数字结尾的最长公共自序列长度。
状态表示: f [ i ] = m a x { f [ j ] + 1 ∣ j ∈ { j ∣ a [ j ] < a [ i ] } } f[i]=max\{f[j]+1|j∈\{j|a[j]<a[i]\}\} f[i]=max{
f[j]+1∣j∈{
j∣a[j]<a[i]}}
代码:
#include<iostream>
using namespace std;
const int N = 1005;
int a[N],f[N];
int main(){
int n;
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=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]);
}
cout<<res<<endl;
return 0;
}
最长公共子序列
思路:
状态表示: f [ i ] [ j ] f[i][j] f[i][j]表示a字符串的前i个字符和b字符串的前j个字符的最长公共自序列。
状态表示: f [ i ] [ j ] = m a x { f [ i − 1 ] [ j − 1 ] , f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] , f [ i − 1 ] [ j − 1 ] + 1 } f[i][j]=max\{f[i-1][j-1],f[i-1][j],f[i][j-1],f[i-1][j-1]+1\} f[i][j]=max{
f[i−1][j−1],f[i−1][j],f[i][j−1],f[i−1][j−1]+1}
分别表示不包括i和j位字符,选包括j位字符,包括i位字符,包括i位和j位字符。
代码:
#include<iostream>
using namespace std;
const int N = 1e3+5;
int n,m,f[N][N];
char a[N],b[N];
int main(){
int res=0;
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][j-1],f[i-1][j]);
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
cout<<f[n][m]<<endl;
return 0;
}
区间动态规划
石子合并
思路:
状态表示: f [ l ] [ r ] f[l][r] f[l][r]表示合并从i到j区间的石子的最小代价。
状态表示: f [ l ] [ r ] = m i n { f [ l ] [ k ] + f [ k + 1 ] [ r ] + s u m ( a [ l ] , . . . , a [ r ] ) } , k ∈ [ l , r − 1 ] f[l][r]=min\{f[l][k]+f[k+1][r]+sum(a[l],...,a[r])\},k∈[l,r-1] f[l][r]=min{
f[l][k]+f[k+1][r]+sum(a[l],...,a[r])},k∈[l,r−1]
一般区间长度从小到大循环。
代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 305;
int a[N],f[N][N],s[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
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 j=l;j<r;j++){
f[l][r]=min(f[l][r],f[l][j]+f[j+1][r]+s[r]-s[l-1]);
}
}
}
cout<<f[1][n]<<endl;
return 0;
}