Interval DP | 2: Merge stones on the ring - Example: Merge stone (ring)

This article is in the interval DP | 1: Matrix chain multiplication problem (including optimization) - Example: Matrix Chain, merge stones  on the upgrade (recommended to walk through the link article). A change from the chain ring, but also the nature of the problem dp interval, the interval of the ring and then resolved into the chain can be any.

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

A bar to turn into a mechanical operation when the wire loop of this question: is n considering combined linear stones, will increase the power of a time complexity, not cost-effective, there is a better method below. (In fact, think about it, the combined n stones invocation linear model, there are actually a lot of double counting)


Article directory

Method One: Direct Gang! The annular array considerations - time complexityO (n ^ 3)

Method two: the circle is a linear - time complexityO (n ^ 3)

         Method three: optimized version of the circle straight - time complexity\bg_white O(n^2)

end 



Method One: Direct Gang! The annular array considerations - time complexityO (n ^ 3)

Since it is circular, we will consider an array of ring to go to, just because a variety of critical issues, the code is more complex, pay attention to details - (it is too trivial a! Wrote two hours to put bug cleaned up, shed bitter tears, so then improved method, see method two )

Compared to the original method, the core is a three-part cycle, but due to the problems connected to the annular ends, the second and third layers are required change cycle:

  • The first layer loop: length l = 2..n
  • The second layer loop: the discussion of each stack stones length l  p_{i..j}: i = 1..n, computing j = (i + l + 1 )% n ( this sum is the loop body more complicated, it is recommended to write a single calcSum function calculation)
  • The third layer loop: determining an optimal dividing point k = i..j-1

Ans filled array corresponding to the two methods are different: 

Code:

Pay attention to critical issues! Pay attention to critical issues trivial! Great attention to the critical issue of trivial! ! !

#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));
}

Method two: the circle is a linear - time complexityO (n ^ 3)

To facilitate traversal, consider the circle linear : the circular cut - 1,2,3 assumed gravel heap, the heap of stones cut 1,2,3,1,2, then if the original array p of length n, then we cut through operation, the length becomes 2n - 1. Then there is the problem of linear stones and merge the same.

The only difference is: for linear 2n - 1 A stone stack, our results are not selected L = 2n - 1, but the minimum value L n = all results.

Code:

(On linear unmodified stones issues has been combined, it is also time complexity 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;
}

Method three: optimized version of the circle straight - time complexity\bg_white O(n^2)

With interval DP | 1: Matrix chain multiplication problem (including optimization) - Example: Matrix Chain, merge stones improvement methods, the new optimal decision point, to divide the storage array.

Code:

#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;
}

Add a little more difficult - while seeking the maximum and minimum

Stone merger

Achievement 10 opening time 2020 March 24 Tuesday, 23:15
discount 0.8 Discount Time 2020 April 21 Tuesday, 23:55
Allow late no Closing time 2020 April 21 Tuesday, 23:55

Description of the problem: a circle around the playground now placed stones piled n Stone is to have a predetermined order to merge into each bunch can only select two adjacent stones piled merge into a pile and the new pile. number of stone the combined score for the note. try to design an algorithm to calculate the n combined into a pile of stones piled minimum score and maximum score.

Algorithm design: For a given n gravel pile, merge into a pile calculating the minimum and maximum score score.

Data input: line 1 is the positive integer n, 1 <= n <= 100, expressed stones piled row n has the second number n, n represents the number of each stack stones.

Output Results: Line 1 is the minimum score, the maximum score is the second row.

  Test input Expected output time limit Memory Limit Additional process
Test Case 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 ↵
Displayed as text
  1. 5913↵
  2. 24595↵
1 second 64M 0

Above we only discussed minimized, and that the need will be the maximum and minimum output situation. In fact, the maximum and minimum routine is exactly the same, just change the sign about it at the time of comparison. And: seeking at maximum, the minimum required for the above optimization unavailable quadrilateral inequality needs to traverse honestly.

AC direct attach codes:

//
// 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);
}


Have any questions please review the exchange, if you wish this article helpful little bit of praise, hee hee ~  



end 

No personal welcome attention to the public "  chicken wings Programming" , here is a serious and well-behaved code agricultural one.

---- do the most well-behaved blog er, do the most solid programmer ----

Aims to carefully write each article, usually aggregated into notes will push updates -

Here Insert Picture Description

Published 138 original articles · won praise 63 · views 10000 +

Guess you like

Origin blog.csdn.net/weixin_43787043/article/details/104994756