- 状态表示:合并(l)到(r)这一区间需要的最小代价
- 状态计算:
- 枚举区间长度
- 枚举分割点,将所有状态进行划分
#include <iostream>
using namespace std;
const int N = 310;
int a[N], s[N], dp[N][N];
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
// 枚举区间长度
for(int len = 2; len <= n; len++) {
// 确定左右端点
for(int i = 1; i + len - 1 <= n; i++) {
int l = i, r = i + len - 1;
dp[l][r] = 1e9;
// 枚举决策点
for(int k = l; k < r; k++)
dp[l][r] = min (
dp[l][r],
dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]
);
}
}
cout << dp[1][n];
return 0;
}
环形DP
#include <iostream>
using namespace std;
const int N = 210, M = N << 1;
int a[N], s[M], h[M];
int dp[N][N], f[N][N];
int main() {
int n; cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
for(int i = 0; i < 2 * n; i++) s[i] = a[i % n] + s[i - 1];
// for(int i = 0; i < 2 * n; i++) cout << s[i] << " ";
// cout << endl;
for(int g = 0; g < n; g++) {
for(int len = 2; len <= n; len++) {
for(int i = g; i + len - 1 < 2 * n; i++) {
int l = i, r = i + len - 1;
dp[l][r] = 1e9, f[l][r] = -2e9;
for(int k = l; k < r; k++) {
dp[l][r] = min(
dp[l][r],
dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]
);
f[l][r] = max(
f[l][r],
f[l][k] + f[k + 1][r] + s[r] - s[i - 1]
);
// cout << l << " " << r << " " << dp[l][r] << " " << endl;;
}
}
}
}
int mi = 1e9, mx = -1;
for(int i = 0; i < n; i++) {
mi = min(mi, dp[i][i + n - 1]);
mx = max(mx, f[i][i + n - 1]);
}
cout << mi << endl << mx;
return 0;
}
- 区间DP + 高精度算法
- 状态表示:从顶点(i)到(j)之间划分成(N - 2)个互不相交的三角形的集合
- 状态计算: (dp[i][j] = min(dp[i][j], dp[i][k] * dp[k][j] + w[i] * w[k] * w[j])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 55, M = 100, INF = 1e9;
int n;
LL w[N];
string dp[M][M];
string add(string a, string b) {
if(a.size() < b.size()) return add(b, a);
vector<LL> x, y;
string ans = "";
for(LL i = a.length() - 1; i >= 0; i--) x.push_back(a[i] - '0');
for(LL i = b.length() - 1; i >= 0; i--) y.push_back(b[i] - '0');
LL t = 0;
for(LL i = 0; i < (int)x.size(); i++) {
t += x[i];
if(i < (int)y.size()) t += y[i];
ans += ((t % 10) + '0');
t /= 10;
}
if(t) ans += "1";
reverse(ans.begin(), ans.end());
return ans;
}
string mul(string a, LL b) {
vector<LL> x;
string ans = "";
for(LL i = a.length() - 1; i >= 0; i--) x.push_back(a[i] - '0');
LL t = 0;
for(LL i = 0; i < (int)x.size() || t; i++) {
if(i < (int)x.size()) t += x[i] * b;
ans += ((t % 10) + '0');
t /= 10;
}
while(ans.back() == '0' && ans.size() > 1) ans.pop_back();
reverse(ans.begin(), ans.end());
return ans;
}
string get_min(string x, string y) {
if (x.size() == 0) return y;
if (y.size() == 0) return x;
if (x.size() > y.size()) return y;
else if (x.size() < y.size()) return x;
return x < y ? x : y;
}
int main() {
cin >> n;
for(LL i = 1; i <= n; i++) cin >> w[i];
for(LL len = 3; len <= n; len ++)
for(LL l = 1; l + len - 1 <= n; l++) {
LL r = l + len - 1;
dp[l][r] = "10000000000000000000000000000000";
for(LL k = l + 1; k < r; k++) {
string tmp = "1";
tmp = mul(mul(mul(tmp, w[l]), w[k]), w[r]);
tmp = add(add(tmp, dp[l][k]), dp[k][r]);
dp[l][r] = get_min(dp[l][r], tmp);
}
}
cout << dp[1][n];
return 0;
}
区间DP记录方案
- 状态表示:中序遍历为i~j的二叉树的得分的最大值
- 以根节点的位置作为集合划分的依据
- 同时记录每颗子树的根
#include <iostream>
#include <cstring>
using namespace std;
const int N = 30;
int w[N], dp[N][N], g[N][N];
void dfs(int x, int y) {
if(x > y) return ;
int k = g[x][y];
cout << k << " ";
dfs(x, k - 1);
dfs(k + 1, y);
}
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> w[i];
for(int len = 1; len <= n; len ++) {
for(int l = 1; len + l - 1 <= n; l++) {
int r = l + len - 1;
// cout << len << " " << l << " " << r << endl;
if(len == 1) {
dp[l][r] = w[l];
g[l][r] = l;
} else {
// 枚举根的位置
for(int k = l; k <= r; k++) {
// k == l, 说明左子树为空
int left = (k == l) ? 1 : dp[l][k - 1];
// 同理,k == r, 说明右子树为空
int right = (k == r) ? 1 : dp[k + 1][r];
if(dp[l][r] < left * right + w[k]) {
dp[l][r] = left * right + w[k];
// 记录当前子树的根节点
g[l][r] = k;
}
}
}
}
}
cout << dp[1][n] << endl;
dfs(1, n);
return 0;
}