区间型动态规划的典型例题是石子归并,同时使用记忆化搜索实现区间动归是一种比较容易实现的方式,避免了循环数组实现的时候一些边界的判断
n堆石子排列成一条线,我们可以将相邻的两堆石子进行合并,合并之后需要消耗的代价为这两堆石子的质量之和,问最小的合并代价
状态转移方程很容易给出:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[i][j])
因为要计算区间和,考虑前缀和进行预处理
然后我们给出用记忆化搜索形式实现的代码,这里的记忆化搜索形式可以作为后续问题的一个模板
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF=0x3f3f3f3f; 6 const int maxn=105; 7 int n; 8 int w[maxn]; 9 int g[maxn]; //前缀和 10 int f[maxn][maxn]; 11 int dfs(int l,int r) 12 { 13 if(l==r) return 0; 14 if(f[l][r]!=INF) 15 return f[l][r]; 16 int tmp=INF; 17 for(int i=l;i<r;i++) 18 tmp=min(tmp,dfs(l,i)+dfs(i+1,r)+g[r]-g[l-1]); 19 if(tmp<f[l][r]) 20 f[l][r]=tmp; 21 return f[l][r]; 22 } 23 int main() 24 { 25 cin>>n; 26 for(int i=1;i<=n;i++) 27 { 28 cin>>w[i]; 29 g[i]=g[i-1]+w[i]; 30 } 31 for(int i=1;i<=n;i++) 32 for(int j=1;j<=n;j++) 33 f[i][j]=0x3f3f3f3f; 34 cout<<dfs(1,n)<<endl; 35 return 0; 36 }
这个问题还是比较显然的,我们考虑另一个问题,那就是环形动态规划
其实环形动态规划也是区间型,只不过区间首尾相接
此时使用记忆化搜索实现,其实是不容易的
典型例题是能量项链
先给出状态转移方程:
f[i][j]=max(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k])
由于每一种区间问题的价值计算方式不一样,可能采用不同的优化形式,本题直接计算即可
然后我们给出使用循环数组方式实现的一个固定的格式,所有的区间型动态规划都可以采取这样的形式来实现
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn=1005; 5 int n; 6 int a[maxn]; 7 long long ans=0; 8 int f[maxn][maxn]; 9 void dp() 10 { 11 for(int l=2;l<=n;l++) //区间长度 12 for(int i=1;i<=n*2-l+1;i++) //区间起点 13 { 14 int j=i+l; //区间终点 15 for(int k=i+1;k<=j-1;k++) //区间中任意点 16 f[i][j]=max(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k]); 17 } 18 for(int i=1;i<=n;i++) 19 if(ans<f[i][i+n]) 20 ans=f[i][i+n]; 21 } 22 int main() 23 { 24 cin>>n; 25 for(int i=1;i<=n;i++) 26 { 27 cin>>a[i]; 28 a[n+i]=a[i]; 29 } 30 dp(); 31 cout<<ans; 32 return 0; 33 }
分别枚举区间的长度,区间的起点和区间中的任意点就好了