区间dp:顾名思义就是在区间上进行动态规划,通过合并小区间求解一段区间上的最优解。
常见模板:
for(int len=1;len<n;len++){//区间长度
for(int be=1;be+len<=n;be++){//起点
int en=be+len;//终点
for(int j=be;j<en;j++){//割点
dp[be][en]=min(dp[be][en],dp[be][j]+dp[j+1][en]+割点代价);(max也可以)
}
}
}
http://www.51nod.com/Challenge/Problem.html#!#problemId=1021
1021 石子归并
输入
第1行:N(2 <= N <= 100) 第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
输出
输出最小合并代价
输入样例
4
1
2
3
4
输出样例
19
解题思路:很明显割点代价为前缀和:sum【en】-sum【be-1】//en为该区间的终点,be为起点
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
#define ri register int
typedef long long ll;
inline ll gcd(ll i,ll j){
return j==0?i:gcd(j,i%j);
}
inline ll lcm(ll i,ll j){
return i/gcd(i,j)*j;
}
inline void output(int x){
if(x==0){putchar(48);return;}
int len=0,dg[20];
while(x>0){dg[++len]=x%10;x/=10;}
for(int i=len;i>=1;i--)putchar(dg[i]+48);
}
inline void read(int &x){
char ch=x=0;
int f=1;
while(!isdigit(ch)){
ch=getchar();
if(ch=='-'){
f=-1;
}
}
while(isdigit(ch))
x=x*10+ch-'0',ch=getchar();
x=x*f;
}
const int maxn=105;
ll dp[maxn][maxn];
ll sum[maxn];
ll a[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld",&a[i]);
sum[i+1]=sum[i]+a[i];
}
for(int i=0;i<maxn;i++){
for(int j=0;j<maxn;j++){
dp[i][j]=1e18;
}
}
for(int i=0;i<maxn;i++){
dp[i][i]=0;
}
for(int len=1;len<n;len++){//区间长度
for(int be=1;be+len<=n;be++){//起点
int en=be+len;//终点
for(int j=be;j<en;j++){//割点
dp[be][en]=min(dp[be][en],dp[be][j]+dp[j+1][en]+sum[en]-sum[be-1]);
}
}
}
cout<<dp[1][n];
return 0;
}
四边形不等式优化:
我们可以知道,没有优化的区间dp时间复杂度为O(n^3),我们可以使用四边形不等式优化时间复杂度为O(n^2)。
这里直接给出四边形不等式的定理:
区间包含性:如果i<=j<m<=n,则满足w【j】【m】<=w【i】【n】
四边形不等式:如果i<=j<m<=n,满足w【i】【m】+w【j】【n】<=w【i】【n】+w【j】【m】(交叉小于包含)
我们假设w函数为割点代价同时满足区间包含性和四边形不等式,那么dp函数也满足四边形不等式。
我们定义m【i】【j】为dp【i】【j】取得最优解时候的割点的坐标
此时有:如果dp满足四边形不等式,有m【i】【j】<=m【i】【j+1】<=m【i+1】【j+1】
关于该定理的证明,有兴趣的可以看这篇博客:点击
接下来用四边形不等式来优化上一道题。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
#define ri register int
typedef long long ll;
inline ll gcd(ll i,ll j){
return j==0?i:gcd(j,i%j);
}
inline ll lcm(ll i,ll j){
return i/gcd(i,j)*j;
}
inline void output(int x){
if(x==0){putchar(48);return;}
int len=0,dg[20];
while(x>0){dg[++len]=x%10;x/=10;}
for(int i=len;i>=1;i--)putchar(dg[i]+48);
}
inline void read(int &x){
char ch=x=0;
int f=1;
while(!isdigit(ch)){
ch=getchar();
if(ch=='-'){
f=-1;
}
}
while(isdigit(ch))
x=x*10+ch-'0',ch=getchar();
x=x*f;
}
const int maxn=105;
ll dp[maxn][maxn];
ll sum[maxn];
ll a[maxn];
ll m[maxn][maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld",&a[i]);
sum[i+1]=sum[i]+a[i];
}
for(int i=0;i<maxn;i++){
for(int j=0;j<maxn;j++){
dp[i][j]=1e18;
}
}
for(int i=0;i<maxn;i++){
dp[i][i]=0;
m[i][i]=i;
}
for(int len=1;len<n;len++){//区间长度
for(int be=1;be+len<=n;be++){//起点
int en=be+len;//终点
for(int j=m[be][en-1];j<=m[be+1][en];j++){//割点 割点区间长度为en-be-1,dp区间为en-be,所以直接调用即可
// dp[be][en]=min(dp[be][en],dp[be][j]+dp[j+1][en]+sum[en]-sum[be-1]);
if(dp[be][en]>=(dp[be][j]+dp[j+1][en]+sum[en]-sum[be-1])){
dp[be][en]=dp[be][j]+dp[j+1][en]+sum[en]-sum[be-1];
m[be][en]=j;
}
}
}
}
cout<<dp[1][n];
return 0;
}
String painter
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7155 Accepted Submission(s): 3464
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
#include<stdio.h> #include<iostream> #include<cstring> using namespace std; const int maxn=1005; int dp[maxn][maxn]; char ch[maxn],ch1[maxn]; int ss[maxn]; int main(){ while(~scanf("%s%s",ch+1,ch1+1)){ int len=strlen(ch+1); // printf("%s %s",ch+1,ch1+1); memset(dp,0,sizeof(dp)); for(int i=1;i<=len;i++){ dp[i][i]=1; } for(int l=1;l<=len;l++){ for(int be=1;be+l<=len;be++){ int en=be+l; dp[be][en]=dp[be+1][en]+1; for(int k=be+1;k<=en;k++){ if(ch1[be]==ch1[k])dp[be][en]=min(dp[be+1][k]+dp[k+1][en],dp[be][en]); } } } for(int i=1;i<=len;i++){ if(ch[i]==ch1[i]) ss[i]=ss[i-1]; else{ ss[i]=dp[1][i]; for(int k=1;k<i;k++){ ss[i]=min(ss[i],ss[k]+dp[k+1][i]); } } } // for(int i=1;i<=len;i++) printf("%d\n",ss[len]); } return 0; }