間隔DP | 2:リング上のマージ石 - 例:マージ石(リング)

この記事ではである区間DP | 1:(最適化を含む)マトリックスチェーン乗算問題-例:マトリックスチェーン、マージ石  アップグレードに関する(リンク記事を歩くことを推奨します)。チェーンに分解後、チェーンリングからの変更はなく、問題のDP間隔の性質上、リングの間隔とは、どんなことができます。

环上的合并石子问题:环形排列着N堆石子,现在要将石子合并成一堆。规定如下:每次只能将相邻的两堆石子合并,合并两堆石子所花费的时间为两堆石子的数量和。求将N堆石子合并成一堆最小花费的时间。(石子分为n堆,石子的数量存储在数组p[0..n-1]中)

この質問のワイヤループをするときバーが機械的な動作に変身する:nは石の線形組み合わせを検討している時間の複雑さの力を増加させる、費用対効果ではない、下記のより良い方法があります。(線形モデルの呼び出しn個組み合わせた石、それについて考え、実際には、ダブルカウントの多くは実際にあります)


記事のディレクトリ

方法の一つ:直接ギャング!環状アレイ上の考慮事項 - 時間計算O(N ^ 3)

方法2:円は直線的である - 時間の複雑O(N ^ 3)

         方法3:ストレート円の最適化バージョン - 時間複雑\ bg_whiteはO(n ^ 2)

終わり 



方法の一つ:直接ギャング!環状アレイ上の考慮事項 - 時間計算O(N ^ 3)

それは円形であるので、我々は重要な問題の様々な、コードは、細部に注意を払うより複雑であるという理由だけで、に行くために、リングの配列を検討します- (それはあまりにも些細なAです!書いた2時間をバグクリーンアップ、小屋を置くこと苦い涙、そうして改良された方法は、方法2を参照してください

元の方法と比較して、コアは、3つの部分からなるサイクルであるが、環状の端部に接続された問題のために、第二及び第三の層は、変更サイクルを必要としています。

  • 第一層ループ:長さL = 2..n
  • 第二層ループ:各スタック石長lの議論  P_ {i..j}:私がjを計算する、1..nのを= =(I + L + 1 )%N( この合計は、より複雑なループ本体であり、単一calcSumを記述することが推奨されます関数の計算)
  • 第3層ループ:点k = i..j-1分割最適を決定します

二つの方法に対応ANS充填配列が異なっています。 

コードの実装:

重要な問題に注意を払います!些細な重要な問題に注意して!些細の重要な問題に大注目!

#define N 100

#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;

/* 合并石子问题:环形排列着N堆石子,现在要将石子合并成一堆。
 * 规定如下:每次只能将相邻的两堆石子合并,合并两堆石子所花费的时间为两堆石子的数量和。
 * 求将N堆石子合并成一堆最小花费的时间。(石子分为n堆,石子的数量存储在数组p[0..n-1]中)*/

int ans[N][N] = {0};
int s = 0;  //所有石子堆的总和(提前计算好)

/* 计算p[i..j]的石子总和 (由于是环形,j可以小于i)*/
int calcSum(int p[], int l, int r, int n) {
    /* 特殊考虑完整(包含了全部石堆)的情况 */
    if (l - r == 1 || r - l + 1 == n)
        return s;
    /* 正常计算 */
    int sum = 0;
    for (int i = l; i != (r + 1) % n; i = (i + 1) % n)
        sum += p[i];
    return sum;
}

int MergeStone(int p[], int n) {

    int min_ans = INT_MAX;
    /* 石子堆的个数:从1到n */
    for (int l = 2; l <= n; l++) {
        /* 讨论l个石子的石子堆 p[i..j](由于是环形,j可以小于i) */
        for (int i = 0; i < n; i++) {
            int j = (i + l - 1) % n;
            int sum = calcSum(p, i, j, n);
            ans[i][j] = INT_MAX;
            /* 依次讨论每一个分割点d:将石子堆p[i..j]分成p[i..k]和 A[k+1..j] */
            for (int k = i; k != j; k = (k + 1) % n)
                ans[i][j] = min(ans[i][k] + ans[(k + 1) % n][j] + sum, ans[i][j]);
            /* l = n 即我们需要的答案范围,找出最小值 */
            if(l == n) {
                min_ans = min(ans[i][j], min_ans);
            }
        }
    }
    return min_ans;
}

int main() {
    int n = 4;
    int p[] = {4, 2, 3, 4};
    for (int i = 0; i < n; i++)
        s += p[i];
    printf("%d\n", MergeStone(p, n));
}

方法2:円は直線的である - 時間の複雑O(N ^ 3)

トラバーサルを容易にするために、検討線形円を:円形カット- 1,2,3-想定砂利・ヒープを、石のヒープは1,2,3,1,2を切断し、もし元の配列P長さnの、我々は動作を切断、長さ2Nになる- 1。そして、そこにリニア石の問題があり、同じマージ。

我々の結果は、L = 2nの選択されていない、1 A石スタック - - リニア2nのために1が、最小値LをN =すべての結果:唯一の違いはあります。

コードの実装:

(線形変更されていない石の問題に結合された、それはまた、時間複雑ですO(N ^ 3)

#define MAX 100

#include <cstdio>
#include <climits>
#include <algorithm>

using namespace std;
/* 合并石子问题:环形排列着N堆石子,现在要将石子合并成一堆。
 * 规定如下:每次只能将相邻的两堆石子合并,合并两堆石子所花费的时间为两堆石子的数量和。
 * 求将N堆石子合并成一堆最小花费的时间。(石子分为n堆,石子的数量存储在数组p[0..n-1]中)*/
int ans[2 * MAX][2 * MAX] = {0};

/* 将环形的石子化为线形 */
void GetList(int p[], int n) {
    int j = 0;
    for (int i = n; i < 2 * n - 1; i++)
        p[i] = p[j++];
}

int MergeStone(int p[], int n) {

    GetList(p, n);  //化圆为直
    int min_ans = INT_MAX;
    int N = 2 * n - 1;// 线形中石子堆个数看做 2n - 1
    /* 石子堆的个数:从1到n */
    for (int l = 2; l <= n; l++) {
        /* 讨论l个石子的石子堆 p[i..j] */
        for (int i = 0; i < N - l + 1; i++) {
            int j = i + l - 1;
            /* 计算石子堆p[i..j]的总数 */
            int sum = 0;
            for (int t = i; t <= j; t++)
                sum += p[t];

            /* 依次讨论每一个分割点d:将石子堆p[i..j]分成p[i..k]和 A[k+1..j] */
            ans[i][j] = INT_MAX;
            for (int k = i; k < j; k++)
                ans[i][j] = min(ans[i][k] + ans[k + 1][j] + sum, ans[i][j]);
            /* l = n 即我们需要的答案范围,找出最小值 */
            if (l == n) {
                min_ans = min(ans[i][j], min_ans);
            }
        }
    }
    return min_ans;
}

方法3:ストレート円の最適化バージョン - 時間複雑\ bg_whiteはO(n ^ 2)

間隔DP | 1:(最適化を含む)マトリックスチェーン乗算問題-例:マトリックスチェーン、マージ石の改善方法、新しい最適な決定ポイント、ストレージアレイを分割します。

コードの実装:

#define MAX 100

#include <cstdio>
#include <climits>
#include <algorithm>

using namespace std;
/* 合并石子问题:环形排列着N堆石子,现在要将石子合并成一堆。
 * 规定如下:每次只能将相邻的两堆石子合并,合并两堆石子所花费的时间为两堆石子的数量和。
 * 求将N堆石子合并成一堆最小花费的时间。(石子分为n堆,石子的数量存储在数组p[0..n-1]中)*/

// 改进版2!!!!!
int ans[2 * MAX][2 * MAX] = {0};
int divide[2 * MAX][2 * MAX] = {0};

/* 将环形的石子化为线形 */
void GetList(int p[], int n) {
    int j = 0;
    for (int i = n; i < 2 * n - 1; i++)
        p[i] = p[j++];
}

void initDivideArray(int n) {
    for (int i = 0; i < n; i++)
        divide[i][i] = i;
}

int MergeStone(int p[], int n) {

    GetList(p, n);  //化圆为直
    int min_ans = INT_MAX;
    int N = 2 * n - 1;// 线形中石子堆个数看做 2n - 1
    initDivideArray(N);  //初始化divide数组

    /* 石子堆的个数:从1到n */
    for (int l = 2; l <= n; l++) {
        /* 讨论l个石子的石子堆 p[i..j] */
        for (int i = 0; i < N - l + 1; i++) {
            int j = i + l - 1;
            /* 计算石子堆p[i..j]的总数 */
            int sum = 0;
            for (int t = i; t <= j; t++)
                sum += p[t];

            /* 依次讨论每一个分割点d:将石子堆p[i..j]分成p[i..k]和 A[k+1..j] */
            ans[i][j] = INT_MAX;
            for (int temp, k = divide[i][j - 1]; k <= divide[i + 1][j]; k++) {
                temp = ans[i][k] + ans[k + 1][j] + sum;
                if (temp < ans[i][j]) {
                    ans[i][j] = temp;
                    divide[i][j] = k;
                }
            }

            /* l = n 即我们需要的答案范围,找出最小值 */
            if (l == n) {
                min_ans = min(ans[i][j], min_ans);
            }
        }
    }
    return min_ans;
}

少し難しく追加 - 最大値と最小値を求めながら、

石の合併

実績 10 オンタイム 2020年3月24日(火)23:15
割引 0.8 ディスカウント時間 2020年4月21日(火)夜11時55
後半許可 ノー 閉会時間 2020年4月21日(火)夜11時55

問題の説明:遊び場の周りの円今置か石が積まN石のみパイルと新しいパイルにマージを重ね隣接する二つの石を選択することができ、各束に合流する所定の順序を有することです。石ノートの組み合わせスコアの数が最小スコアと最大スコア積み上げた石の山に組み合わせるのnを計算するためのアルゴリズムを設計してみてください。

アルゴリズム設計:N所与砂利パイルの場合、最小値と最大スコアのスコアを計算するパイルに合流。

データ入力:ライン1は、正の整数nは、1 <= N <= 100、発現石積み重ね行であるn個の第2の数を有するN、N-各スタック石の数を表します。

出力結果:1行目は、最小スコアが、最大スコアが第二の行です。

  テスト入力 予想される出力 タイムリミット メモリ制限 追加のプロセス
テストケース1  
  1. 36↵
  2. 53 49 2 9 9 30 2 35 1 46 39 46 42 33 13 41 35 57 38 59 15 40 18 6 46 30 53 31 34 57 41 20 1 42 59 46 45↵
テキストとして表示
  1. 5913↵
  2. 24595↵
1秒 64M 0

我々は唯一の最小化、および必要性が最大と最小の出力状況になることを述べた上で。実際には、最大値と最小値のルーチンは、単に比較の時に、それについての符号を変え、まったく同じです。そして:最大で求め、最小値は、上記のために必要な最適化を使用できない四辺形不等式正直に横断する必要性を。

ACは、直接コードを添付します:

//
// Created by A on 2020/3/20.
//
#include <cstdio>
#include <cmath>
#include <climits>
#include <algorithm>

#define MAX 300

using namespace std;
/* 合并石子问题:环形排列着N堆石子,现在要将石子合并成一堆。
 * 规定如下:每次只能将相邻的两堆石子合并,合并两堆石子所花费的时间为两堆石子的数量和。
 * 求将N堆石子合并成一堆最小花费的时间。(石子分为n堆,石子的数量存储在数组p[0..n-1]中)*/

// 改进版2!!!!!
int min_ans[2 * MAX][2 * MAX] = {0};
int max_ans[2 * MAX][2 * MAX] = {0};
int min_divide[2 * MAX][2 * MAX] = {0};
int max_divide[2 * MAX][2 * MAX] = {0};


/* 将环形的石子化为线形 */
void GetList(int p[], int n) {
    int j = 0;
    for (int i = n; i < 2 * n - 1; i++)
        p[i] = p[j++];
}

void initDivideArray(int n) {
    for (int i = 0; i < n; i++) {
        min_divide[i][i] = i;
        max_divide[i][i] = i;
    }

}

void MergeStone(int p[], int n) {

    GetList(p, n);  //化圆为直
    int N = 2 * n - 1;// 线形中石子堆个数看做 2n - 1
    initDivideArray(N);  //初始化divide数组

    /* 石子堆的个数:从1到n */
    for (int l = 2; l <= n; l++) {
        /* 讨论l个石子的石子堆 p[i..j] */
        for (int i = 0; i < N - l + 1; i++) {
            int j = i + l - 1;
            /* 计算石子堆p[i..j]的总数 */
            int sum = 0;
            for (int t = i; t <= j; t++)
                sum += p[t];

            /* 依次讨论每一个分割点d:将石子堆p[i..j]分成p[i..k]和 A[k+1..j] */
            min_ans[i][j] = INT_MAX;
            for (int temp, k = min_divide[i][j - 1]; k <= min_divide[i + 1][j]; k++) {
                temp = min_ans[i][k] + min_ans[k + 1][j] + sum;
                if (temp < min_ans[i][j]) {
                    min_ans[i][j] = temp;
                    min_divide[i][j] = k;
                }
            }
            max_ans[i][j] = INT_MIN;
            for (int temp, k = i; k < j; k++) {
                temp = max_ans[i][k] + max_ans[k + 1][j] + sum;
                if (temp > max_ans[i][j]) {
                    max_ans[i][j] = temp;
                    max_divide[i][j] = k;
                }
            }
        }
    }
}

int main() {
    int n, p[MAX];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &p[i]);

    MergeStone(p, n);
    int maxResult = INT_MIN, minResult = INT_MAX;
    for (int i = 0, j = n - 1; i < n; i++, j++) {
        maxResult = max(maxResult, max_ans[i][j]);
        minResult = min(minResult, min_ans[i][j]);
    }
    printf("%d\n%d\n", minResult, maxResult);
}


あなたが賞賛の役に立つ少しこの記事を希望する場合はご質問は、交換を確認してください持っている、姫姫〜  



終わり 

公衆への個人的な歓迎の注意ありませんが 手羽先はプログラミング」、ここで深刻な行儀のコード農業の一つがあります。

----は、最も行儀ブログERを行い、ほとんどの固体プログラマが行います----

慎重に、通常のノートに集約各記事を、書くことを目指し、更新をプッシュします -

ここに画像を挿入説明

公開された138元の記事 ウォン称賛63 ビュー10000 +

おすすめ

転載: blog.csdn.net/weixin_43787043/article/details/104994756