1. 程式人生 > 實用技巧 >P1880 [NOI1995]石子合併

P1880 [NOI1995]石子合併

P1880 [NOI1995]石子合併

做過類似的,不過這題稍微有點不一樣:是環不是鏈。

只要把鏈複製一遍原來的鏈的後面,就可以化環為鏈了。

注意題目求的是N堆石子合併,列舉區間長度的時候依然是從2列舉到N。

int a[maxn];
int b[maxn];
//字首和
int dp1[maxn][maxn], dp2[maxn][maxn];

int main()
{
    int N; cin >> N;
    memset(dp1, INF, sizeof(dp1));
    memset(dp2, -INF, sizeof(dp2));
    for (int i = 1; i <= N; i++) {
        dp1[i][i] = dp2[i][i] = dp1[N + i][N + i] = dp2[N + i][N + i] = 0;
    }
   //自己和自己合併得分為0
    for (int i = 1; i <= N; i++) {
        cin >> a[i];
        a[i + N] = a[i];
    }
  //化環為鏈
    for (int i = 1; i <= N + N - 1; i++) {
        b[i] = b[i - 1] + a[i];
    }

    for (int len = 2; len <= N; len++) {
        //列舉長度
        for (int l = 1; l <= N+N - len + 1; l++) {
            //列舉左端點l
            int r = l + len - 1;
            for (int spl = l; spl <= r - 1; spl++) {
                //列舉分割點split
                //表示在spl堆與spl+1堆之間分割
                dp1[l][r] = min(dp1[l][r], dp1[l][spl] + dp1[spl + 1][r]);
                dp2[l][r] = max(dp2[l][r], dp2[l][spl] + dp2[spl + 1][r]);
            }
            dp1[l][r] += b[r] - b[l - 1];
            dp2[l][r] += b[r] - b[l - 1];
        }
    }
    //長度為N的區間全掃一遍取最大/最小值
    int mmax = -1;
    int mmin = INF;
    for (int i = 1; i <= N; i++) {
        mmin = min(mmin, dp1[i][i + N - 1]);
        mmax = max(mmax, dp2[i][i + N - 1]);
    }
    cout << mmin << endl << mmax << endl;
    return 0;
}