本文主要是介绍石子合并-环(区间dp)c++,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
刚才的《石子合并》问题是把石子放成一排,如果石子放成一个环,第 N 堆和第 1 堆相邻,又该怎么做呢?
我们要想办法把它变成之前放成一排的问题,可以发现只要我们把
- a1,a2,a3,⋯,aN
- a2,a3,a4,⋯,aN,a1
- a3,a4,a5,⋯,aN,a1,a2
- …
- aN,a1,a2,⋯,aN−1
这 N 种放成一排的情况都考虑清楚,取其中的最优解,就一定是放成环的样子的最优解。
但如果做 N 次区间 DP 时间复杂度就变成了 O(n4),并且我们可以发现在其中有一些状态是重复计算的。
有一种很常用的方法可以避免这样的重复计算,就是把数组复制一遍,把复制的放在原数组的后边,形成一个新的序列 b,其中的元素是:
a1,a2,a3,⋯,aN,a1,a2,a3,⋯,aN
此时,
- a1,a2,a3,⋯,aN
- 1a2,a3,a4,⋯,aN,a1
- a3,a4,a5,⋯,aN,a1,a2
- …
- aN,a1,a2,⋯,aN−1
这里的每一种情况后对应成了复制完成后数组的一段连续区间。
- a1,a2,a3,⋯,aN 是区间b1∼bN
- a2,a3,a4,⋯,aN,a1 是区间 b2∼bN+1
- a3,a4,a5,⋯,aN,a1,a2 是区间 b3∼bN+2
- …
- aN,a1,a2,⋯,aN−1 是区间bN∼b2×N−1
所以我们只需要正常做区间 DP ,最后求dp[1][N],dp[2][N+1],…,dp[N][2×N−1] 的最小值即可。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int a[205], sum[205], dp[205][205], ans = inf;
int main() {int n;cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];a[i + n] = a[i];}memset(dp, 0x3f, sizeof(dp));for (int i = 1; i <= 2 * n; i++) {sum[i] = sum[i - 1] + a[i];dp[i][i] = 0;}for (int len = 2; len <= n; len++) {for (int i = 1; i <= n; i++) {int j = i + len - 1;for (int k = i; k < j; k++) {dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);}}}for (int i = 1; i <= n; i++) {ans = min(ans, dp[i][i + n - 1]);}cout << ans;return 0;
}
这篇关于石子合并-环(区间dp)c++的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!