区间DP模板题
区间DP模板Code:
for(int len=2;len<=n;len++) { for(int i=1;i<=2*n-1;i++) //区间左端点 { int j = i + len - 1; //区间右端点 for(int k=i;k//断点位置 { f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); } } }
题目描述
在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输入样例#1: 4 4 5 9 4
输出样例#1: 43 54
这个题的数据存储特点有一点代表性:破环成列,把长度为n的环转换为长度为2n-1的列,再进行一次动归。
针对于这个题的n很小,我们就可以用它来代表区间长度,这样O(n ^ 3)也能跑过去了
区间DP的概念就是把一个区间的状态一直分割为它的子区间的状态,一直到这个子区间的状态是显然可求的,最后再将它们综合起来
举个栗子:
f[i][j]中我们可以将[i,j]这一个区间划分为[i,k]和[k + 1,j]这两个区间的总状态再进行一次操作
这个的边界就是[i,k]和[k + 1,j]是显然可求的状态
这个题要求一个最大值和最小值的问题
我们可以显而易见地发现最小值一定小于等于最大值
这样我们可以只建立一个数组先求最小再求最大,节省了两个数组的空间(虽然这不是重点qwq)
Code:
#include#include #include #include using namespace std; int f[300][300]; //节省空间 int s[300]; int n,x,ans = 55312725; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); s[i] = s[i - 1] + x; s[i + n] = s[i]; //把长度开到 2n - 1 } for(int i=1;i ) s[i + n] += s[n]; memset(f,10,sizeof(f)); //初始化 for(int i=1;i<=2*n-1;i++) f[i][i] = 0; for(int len=2;len<=n;len++) { for(int i=1;i<=2*n-1;i++) //区间左端点 { int j = i + len - 1; //区间右端点 for(int k=i;k //断点位置 { f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); } } } for(int i=1;i<=n;i++) ans = min(ans,f[i][i + n - 1]); printf("%d\n",ans); //最小值一定比最大值要小,所以无需更新 for(int len=2;len<=n;len++) { for(int i=1;i<=2*n-1;i++) //区间左端点 { int j = i + len - 1; //区间右端点 for(int k=i;k //断点位置 { f[i][j] = max(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); } } } for(int i=1;i<=n;i++) ans = max(ans,f[i][i + n - 1]); printf("%d\n",ans); return 0; }