南京Oj1225,石子合并,DP-CSDN博客

50 阅读1分钟

南京OJ1225超链接

千万别写成贪心算法了,代码注释中有一些解释

zzuwenjie 2017-3-23 22:17:09  其中那个求区间和的想法比较好。时间复杂度 O(N^3),我班那谁说有NlogN的,反正我不信

\

/*************************************************************\
*dp(i,j)表示从第i堆开始,顺指针的j堆移成一堆的最优得分              *
*Sum(i,j) 第i堆石子到和其后面j堆石子的和(闭区间)                 *
**************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100 + 7;

int preSum[maxn];
int dpMin[maxn][maxn];
int dpMax[maxn][maxn];
int n, stone[maxn];

int sectionSum(int i, int j) {
	if (i + j < n)
		return preSum[i + j] - (i == 0 ? 0 : preSum[i - 1]); //
	return (preSum[n - 1] - preSum[i - 1]) + preSum[(i + j) % n]; //
}//第i堆石子和其后面j堆石子的区间和

void getBest(int& minNum, int& maxNum) { //引用调用
	for (int i = 0; i < n; ++i) // j = 0
		dpMin[i][0] = dpMax[i][0] = 0;
	for (int j = 1; j < n; ++j) //动态规划思想,自底向上求最优
		for (int i = 0; i < n; ++i) {
			dpMin[i][j] = INT_MAX; //最大整型常量
			dpMax[i][j] = 0;
			for (int k = 0; k < j; ++k) {
				dpMin[i][j] = min(dpMin[i][k] + dpMin[(i + 1 + k) % n][j - k - 1] + sectionSum(i, j), dpMin[i][j]);
				dpMax[i][j] = max(dpMax[i][k] + dpMax[(i + 1 + k) % n][j - k - 1] + sectionSum(i, j), dpMax[i][j]);
			} //DP方程代码化
		}
	minNum = dpMin[0][n - 1];
	maxNum = dpMax[0][n - 1];
	for (int i = 1; i < n; ++i) {
		//因为是个环吗,把环断成链有n种方法
		minNum = min(minNum, dpMin[i][n - 1]);
		maxNum = max(maxNum, dpMax[i][n - 1]);
	}
}

int main() {
	//while (~scanf("%d", &n)) { //南京OJ 1225 不允许多组输入,所以用下行代替此行
		scanf("%d", &n);
		for (int i = 0; i < n; ++i)
			scanf("%d", stone + i);
		preSum[0] = stone[0];
		for (int i = 1; i < n; ++i)
			preSum[i] = preSum[i - 1] + stone[i]; //stoneの前缀和
		int minNum, maxNum;
		getBest(minNum, maxNum);
		printf("%d\n%d\n", minNum, maxNum);
	//}
	return 0;
}

\

石子合并问题

时间限制(普通/Java) :  1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 196            测试通过 : 47 \

比赛描述

在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。

对于给定n堆石子,编程计算合并成一堆的最小得分和最大得分。

\

\

输入

输入的第1行是正整数n,1≤n≤100,有n堆石子。第二行有n个数,分别表示每堆石子的个数。

输出

输出的第1行中的数是最小得分;第2行中的数是最大得分。

样例输入

4
4 4 5 9\

样例输出

43
54\

提示

undefined

\

南阳理工学院737 石子合并(一)

直线版的石子合并,每次合并相邻两堆,一次合并花费为两堆石子数和。没有用平行四边形加速版的

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1007, inf = 0x3f3f3f3f;
int preSum[N], dp[N][N];
int main()
{
    int n;
    while(~scanf("%d", &n)) {
        preSum[0] = 0;
        int tmp;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &tmp);
            preSum[i] = preSum[i - 1] + tmp;
        }
        for (int i = 1; i <= n; ++i) dp[i][i] = 0;
        for(int d =  1; d < n; ++d) {
            for (int i = 1, j = d + 1; i + d <= n; ++i, ++j) {
                dp[i][j] = inf;
                for (int k = i; k < j; ++k) {
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
                }
                dp[i][j] += preSum[j] - preSum[i - 1];
            }
        }
        printf("%d\n", dp[1][n]);
    }

    return 0;
}


\

\

\

\