i问题:
经典的石子合并问题,不过现在简化为线性问题,而不是环形问题。
放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
比如
3 4 6 5 4 2
最后得到的结果为:61
分析:
经典的DP算法,代码如下:
其中通项式为:
a[i][j] = min{k | a[i][k] + a[k+1][j] + sum[i...j], k = i...j-1}
其中a[i][j]表示从第i堆到第j堆合并能够取到的最小值,将其分解为两部分,从i到k,以及从k+1到j,再加上两大堆合并的得分。
/*
* nim_merge.cpp
*
* Created on: 2012-11-4
* Author: happier
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
#define MAX 100
#define MAX_VALUE 0x7fffffff
int a[MAX][MAX];
int array[MAX];
int nim_merge(int n)
{
if(n <= 0)
return -1;
for(int i = 1; i <= n; i++)
{
a[i][i] = 0;
}
int i, j, t;
int min;
int sum;
for(int k =1; k <= n-1; k++) //确定步长
{
i = 1; //画斜对角线
j = i+k; //j开始位置,然后i++和j++
//计算i到j的最大值
//a[i][j] = min{k|a[i][k] + a[k+1][j] + sum[i...j], k = i...j-1}
while(i <=n && j <=n)
{
min = MAX_VALUE;
for( t = i; t <j; t++)
{
sum = a[i][t] + a[t+1][j];
if(sum < min)
min = sum;
}
sum = 0;
for( t= i; t <=j; t++)
sum += array[t]; //最后合并的总和
a[i][j] = min + sum;
i++;
j++;
}
}
return a[1][n];
}
int main()
{
int n;
scanf("%d", &n);
for(int i= 1; i <= n; i++)
{
scanf("%d", &array[i]);
}
std::cout << nim_merge(n) << endl;
return 0;
}