[洛谷]P1880 石子合并问题

Problem

portal: P1880 石子合并

Description

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

Input

数据的第1行是正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

Output

输出共2行,第1行为最小得分,第2行为最大得分.

Sample

Sample Input

4
4 5 9 4

Sample Output

43
54

Solution

Analysis

设max_score(start, end)是起点为第start堆石子,终点为第end堆石子的最高得分。
对于这部分石子堆,存在第mid堆石子,使得最后一步的合并为start至mid这部分石子合并的一个石子堆mid+1至end这部分石子合并的一个石子堆的合并,并且左半部分的得分是最高的,右半部分的得分也是最高的。

得到公式
max_score(start,end) = max(max_score(start, mid) + max_score(mid + 1) + total(start, end))   (start != end)
左半部分的最高得分+右半部分的最高得分+最终合并的得分 mid的取值为start一直到end的前一个 (可能存在5->6->1->2->3->4这种情况, 此时mid取5, 6, 1, 2, 3)
max_score(start,end) = 0 (start == end)

根据跨度逐渐增加,最后计算出这些石子堆的最高得分。
最低得分类似,不过每次求最小的。

细节见代码

Code

#include <bits/stdc++.h>
#define MAX_N 105

using namespace std;

int n, cnt[MAX_N];
// 最大得分计算所需要的中间值存储
int max_score[MAX_N][MAX_N];
// 最小得分计算所需要的中间值存储
int min_score[MAX_N][MAX_N];
// 统计从i到j堆石子的总数, i可能会小于j, 此时从i计算到n, 再从1计算到j
int total[MAX_N][MAX_N];

// 当前堆的下一个堆的序号
int next(int x) { 
    if (x == n) return 1;
    else return x + 1;
}

int main(void) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> cnt[i];
    }

 	// 两个for循环, 完善所有起点为第i堆, 终点为第j堆的石子的总数
    for (int i = 1; i<= n; i++) {
    	total[i][i] = cnt[i];
    }
    for (int range = 1; range < n; range++) {
    	for (int i = 1; i <= n; i++) {
    		int j = i + range;
    		if (j > n) j -= n;
			total[i][j] = total[next(i)][j] + cnt[i];
    	}
    }
    
    // 一个石子堆不用合并, 结果为0
    for (int i = 1; i <= n; i++) {
    	max_score[i][i] = 0;
    	min_score[i][i] = 0;
    }

    int start, end;
    int max_result = 0, min_result = INT_MAX;
    // 跨度逐渐增加, 从起点与后面的第一个石子堆合并, 到起点与后面的n-1个, 即所有的石子堆合并
    for (int range = 1; range < n; range++) {
    	int max_value, min_value;
    	// 由于是环形操场, 所以起点不是固定的
    	for (start = 1; start <= n; start++) {
    		end = start + range;
    		if (end > n) end -= n; // 环形操场

    		max_value = 0;
    		min_value = INT_MAX;
    		for (int mid = start; mid != end; mid = next(mid)) {
    			// 使用公式
    			max_value = max(max_value, max_score[start][mid] + max_score[next(mid)][end] + total[start][end]);
    			min_value = min(min_value, min_score[start][mid] + min_score[next(mid)][end] + total[start][end]);
    		}
    		// 存储起点到终点这部分的 最大/最小 值
    		max_score[start][end] = max_value;
    		min_score[start][end] = min_value;

    		// 等所有的石子堆都参与时, 找出起点不同时最大/最小得分的值
    		if (range == n - 1) {
	    		max_result = max(max_result, max_score[start][end]);
	    		min_result = min(min_result, min_score[start][end]);
    		}
    	}
    }
    cout << min_result << endl;
    cout << max_result << endl;
}
posted @ 2019-10-27 20:04  by-sknight  阅读(238)  评论(0编辑  收藏  举报