72: libreoj #10147 区间dp

$des$

将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数 nnn 及每堆的石子数,并进行如下计算:

  1. 选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
  2. 选择一种合并石子的方案,使得做 n−1 次合并得分总和最小。

$sol$

经典区间dp

$code$

#include <bits/stdc++.h>

using namespace std;

const int N = 405;

int f[N][N], w[N], sum[N];
int n;

int main() {
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        cin >> w[i];
        w[i + n] = w[i];
    }
    for(int i = 1; i <= n * 2; i ++) sum[i] = sum[i - 1] + w[i];
    for(int i = 1; i <= n * 2; i ++) for(int j = 1; j <= n * 2; j ++) f[i][j] = (1 << 29);
    for(int i = 1; i <= n * 2; i ++) f[i][i] = 0;
    for(int len = 2; len <= n; len ++) {
        for(int l = 1; l + len - 1 <= n * 2; l ++) {
            int r = l + len - 1;
            for(int k = l + 1; k <= r; k ++) {
                f[l][r] = min(f[l][r], f[l][k - 1] + f[k][r]);
            }
            f[l][r] += sum[r] - sum[l - 1];
        }
    }
    int Answer = (1 << 30);
    for(int i = 1; i <= n; i ++) Answer = min(Answer, f[i][i + n - 1]);
    cout << Answer << "\n";
    for(int i = 1; i <= n * 2; i ++) for(int j = 1; j <= n * 2; j ++) f[i][j] = -1;
    for(int i = 1; i <= n * 2; i ++) f[i][i] = 0;
    for(int len = 2; len <= n; len ++) {
        for(int l = 1; l + len - 1 <= n * 2; l ++) {
            int r = l + len - 1;
            for(int k = l + 1; k <= r; k ++) {
                f[l][r] = max(f[l][r], f[l][k - 1] + f[k][r]);
            }
            f[l][r] += sum[r] - sum[l - 1];
        }
    }
    Answer = -1;
    for(int i = 1; i <= n; i ++) Answer = max(Answer, f[i][i + n - 1]);
    cout << Answer;
    return 0;
}

 

posted @ 2018-10-30 16:52  xayata  阅读(134)  评论(0编辑  收藏  举报