石子合并
石子合并中为了求出从 1 1 1~ n n n合并的最小代价,我们可以尝试枚举最后一次合并的分界线位置,将问题划分为两个子问题,然后递归求解。在此过程中,我们可以求得从 i i i到 j j j石子合并的最小代价,为了求出所有情况,我们按照区间长度从小到大枚举。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 310;
int n;
int s[N];
int f[N][N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> s[i];
s[i] += s[i - 1];
}
for(int len = 1; len < n; len ++)
{
for(int i = 1; i + len <= n; i ++)
{
int l = i, r = i + len;
f[l][r] = 1e9;
for(int k = l; k <= r; k ++)
{
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
}
}
}
cout << f[1][n] << endl;
return 0;
}
记忆化搜索
递归+备忘录
#include <iostream>
#include <cstring>
using namespace std;
const int N = 307;
int a[N], s[N];
int f[N][N];
// 递归函数
int dp(int i, int j) {
if (i == j) return 0; // 判断边界
int &v = f[i][j]; //备忘录,已经得到的状态直接返回
if (v != -1) return v;
v = 1e9;
//枚举分界线
for (int k = i; k <= j - 1; k ++)
v = min(v, dp(i, k) + dp(k + 1, j) + s[j] - s[i - 1]);
return v;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
s[i] += s[i - 1] + a[i];
}
memset(f, -1, sizeof f);
cout << dp(1, n) << endl;
return 0;
}