暂无。。。
暂无。。。
题目1:环形石子合并。
解题思路:已知石子合并的求解方式,关键是如何化解环形。可以将两个相同数组拼起来,答案就是f[1][n], f[2][n+1], f[3][n+2], ..., f[n][2*n-1]
中的最小值。
区间DP的状态的遍历模板为,
for (int len = 1; len <= n; ++len) {
for (int l = 1; l + len - 1 <= 2 * n; ++l) {
int r = l + len - 1;
//进行状态计算操作...
}
}
此处,状态定义f[i][j]
:表示合并[i,j]石子的最小代价。
状态转移为:
f[i][j];
for (int k = i; k < j; ++k) {
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
}
其中s[]
表示前缀和数组。
C++代码如下,
#include
#include
using namespace std;
const int N = 210;
int n;
int w[N * 2];
int s[N * 2];
int f[N * 2][N * 2]; //最小代价
int g[N * 2][N * 2]; //最大代价
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = n + 1; i <= 2 * n; ++i) w[i] = w[i - n];
for (int i = 1; i <= 2 * n; ++i) s[i] = s[i - 1] + w[i];
memset(f, 0x3f, sizeof f); //初始化
memset(g, -0x3f, sizeof g); //初始化
//状态计算
for (int len = 1; len <= n; ++len) {
for (int l = 1; l + len - 1 <= 2 * n; ++l) {
int r = l + len - 1;
if (l == r) f[l][r] = g[l][r] = 0;
else {
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]);
g[l][r] = max(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]);
}
}
}
}
int maxv = -0x3f3f3f3f, minv = 0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
minv = min(minv, f[i][n + i - 1]);
maxv = max(maxv, g[i][n + i - 1]);
}
cout << minv << endl << maxv << endl;
return 0;
}
题目2:能量项链。
解题思路:主要就是把题目的意思理解好,解题方法同题目1。
C++代码如下,
#include
using namespace std;
const int N = 210;
int n;
int w[N];
int f[N][N];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> w[i];
w[i + n] = w[i];
}
//计算状态
for (int len = 3; len <= n + 1; ++len) {
for (int l = 1; l + len - 1 <= 2 * n; ++l) {
int r = l + len - 1;
for (int k = l + 1; k < r; ++k) {
f[l][r] = max(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
}
}
}
int res = 0;
for (int i = 1; i <= n; ++i) res = max(res, f[i][n + i]);
cout << res << endl;
return 0;
}
题目3:凸多边形的划分。
解题思路:区间DP,注意状态定义和转移时的细节。需要用到高精度计算。
C++代码如下(这里就不写高精度版本了,写个普通版本,高精度版本以后再补充),
#include
using namespace std;
const int N = 60;
int n;
int w[N];
int f[N][N];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int len = 3; len <= n; ++len) {
for (int l = 1; l + len - 1 <= n; ++l) {
int r = l + len - 1;
f[l][r] = 0x3f3f3f3f;
for (int k = l + 1; k < r; ++k) {
f[l][r] = min(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
}
}
}
cout << f[1][n] << endl;
return 0;
}
题目4:加分二叉树。
解题思路:区间DP,关键此题需要记录最佳价值对应的具体二叉树。咋做呢?记录每个区间的最佳方案的根结点就行了。
状态定义f[l][r]
:所有中序遍历是[l,r]这一段的二叉树的集合。属性是最大值。
状态转移,以下情况的最大值,
f[l][r] = max(f[l][r], f[l][k - 1] + f[k + 1][r] + w[k])
C++代码如下,
#include
using namespace std;
const int N = 30;
int n;
int w[N];
int f[N][N], g[N][N];
void dfs(int l, int r) {
if (l > r) return;
int root = g[l][r];
cout << root << " ";
dfs(l, root - 1);
dfs(root + 1, r);
return;
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int len = 1; len <= n; ++len) {
for (int l = 1; l + len - 1 <= n; ++l) {
int r = l + len - 1;
if (l == r) {
f[l][r] = w[l];
g[l][r] = l;
} else {
for (int k = l; k <= r; ++k) {
int a = k == l ? 1 : f[l][k - 1];
int b = k == r ? 1 : f[k + 1][r];
int score = a * b + w[k];
if (f[l][r] < score) {
f[l][r] = score;
g[l][r] = k;
}
}
}
}
}
cout << f[1][n] << endl;
dfs(1, n);
return 0;
}
题目5:棋盘分割。
解题思路:区间DP,状态转移那部分没有搞懂,后续再说。
C++代码如下,
#include
#include
#include
#include
using namespace std;
const int N = 15, M = 9;
const double INF = 1e9;
int n, m = 8;
int s[M][M];
double f[M][M][M][M][N];
double X;
int get_sum(int x1, int y1, int x2, int y2) {
return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}
double get(int x1, int y1, int x2, int y2) {
double sum = get_sum(x1, y1, x2, y2) - X;
return sum * sum / n;
}
double dp(int x1, int y1, int x2, int y2, int k) {
double &v = f[x1][y1][x2][y2][k];
if (v >= 0) return v;
if (k == 1) return v = get(x1, y1, x2, y2);
v = INF;
for (int i = x1; i < x2; ++i) {
v = min(v, get(x1, y1, i, y2) + dp(i + 1, y1, x2, y2, k - 1));
v = min(v, get(i + 1, y1, x2, y2) + dp(x1, y1, i, y2, k - 1));
}
for (int i = y1; i < y2; ++i) {
v = min(v, get(x1, y1, x2, i) + dp(x1, i + 1, x2, y2, k - 1));
v = min(v, get(x1, i + 1, x2, y2) + dp(x1, y1, x2, i, k - 1));
}
return v;
}
int main() {
cin >> n;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> s[i][j];
s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
}
}
X = (double)s[m][m] / n;
memset(f, -1, sizeof f);
printf("%.3lf\n", sqrt(dp(1, 1, 8, 8, n)));
return 0;
}