千万别写成贪心算法了,代码注释中有一些解释
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
\
直线版的石子合并,每次合并相邻两堆,一次合并花费为两堆石子数和。没有用平行四边形加速版的
#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;
}
\
\
\
\