洛谷 P1880 [NOI1995]石子合併

題目描述

在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記爲該次合併的得分。

試設計出1個算法,計算出將N堆石子合併成1堆的最小得分和最大得分.

輸入輸出格式

輸入格式:

 

數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.

 

輸出格式:

 

輸出共2行,第1行爲最小得分,第2行爲最大得分.

 

輸入輸出樣例

輸入樣例#1: 複製

4
4 5 9 4

輸出樣例#1: 複製

43
54

解法:區間合併

區間合併的模版經典題(然而我是第一次做)

令dp[i][j]表示將i-j內的所有石子合併成一堆後的最值

那麼顯然我們可以將這個區間劃分爲兩部分,可以由這兩部分組合而來。

dp[i][j] = dp[i][k] + dp[k + 1][j] + sum(i, j) 

得到狀態轉移方程以後我們就要開始考慮怎麼寫了。

顯然在計算長度較大的區間時長度較小的區間已經被計算出來了。

所以我們考慮枚舉長度,再枚舉起始位置即可。

那麼計算一段區間的和我們可以直接用前綴和。

還有一個問題由於這一堆石子是一個環而不是一條鏈,所以我們將原序列擴充一遍,最後枚舉各位置長度爲n區間取到最值即可。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <climits>
#include <queue>
#define pii pair<int, int>
#define vi vector<int>
#define ll long long
#define eps 1e-3
using namespace std;
const int maxn = 1e3 + 10;
ll dpmn[maxn][maxn];//dp[i][j]表示將第i到j堆石子合併能產生的最大值
ll dpmx[maxn][maxn];//dp[i][j]表示將第i到j堆石子合併能產生的最小值
int a[maxn];
ll sum[maxn];
int main()
{
    //freopen("/Users/vector/Desktop/in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];
    }
    for(int i = n + 1; i <= 2 * n; i++)
    {
        a[i] = a[i - n];
        sum[i] = sum[i - 1] + a[i];
    }
    n *= 2;
    for (int len = 2; len <= n / 2; len++)
    {
        for (int i = 1; i <= n - len + 1; i++)
        {
            //區間爲[i, i + len - 1]
            dpmn[i][i + len - 1] = INT_MAX;
            for(int j = i; j < i + len - 1; j++)
            {
                dpmx[i][i + len - 1] = max(dpmx[i][i + len - 1],dpmx[i][j] + dpmx[j + 1][i + len - 1] + sum[i + len - 1] - sum[i - 1]);
                dpmn[i][i + len - 1] = min(dpmn[i][i + len - 1],dpmn[i][j] + dpmn[j + 1][i + len - 1] + sum[i + len - 1] - sum[i - 1]);
            }
        }
    }
    ll mn = INT_MAX, mx = INT_MIN;
    for(int i = 1; i <= n / 2; i++)
    {
        mn = min(mn, dpmn[i][i + n / 2 - 1]);
        mx = max(mx, dpmx[i][i + n / 2 - 1]);
    }
    cout << mn << '\n' << mx << endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章