问题描述和样例
- 当只有一堆石子时 , 无需合并
- m[i][i][0] = 0;
- m[i][i][1] = 0;
由于是一个环, 所以最后的可以和最前边的合并 , 可以用一个2倍于原数组数组存放 , 可以覆盖到循环合并的所有情况
递归求解
由于一堆石子时是基础解 ,所以从2堆石子时向上求解
- m[s][j][1] = ( sum[j] - sum[s - 1] ) + max { m[s][k - 1][1] + m[k][j][1] }
- m[s][j][0] = ( sum[j] - sum[s - 1] ) + min { m[s][k - 1][0] + m[k][j][0] }
- k为所有可能的合并位置
- ( sum[j] - sum[s - 1] ) , 表示此次合并的值 (arr[s] + arr[s+1] + ….. + arr[j] =sum[j] - sum[s - 1] )
代码
#include<stdio.h>const int N = 202;//最大的堆数int arr[N];int n;//实际堆数int m[N][N][2];//m[i][j][0] 合并i-j最小值 m[i][j][1] 合并i-j最大值int sum[N] = {0};//sum[i][j] = i到j的和int main(){scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &arr[i]);arr[n + i] = arr[i];//基础解 只有一堆无需合并m[i][i][0] = 0;m[i][i][1] = 0;}for (int i = 1; i <= 2 * n; i++) {sum[i] = sum[i - 1] + arr[i];}//动态规划for (int r = 2; r <= n; r++) {//合并的长度for (int s = 1; s <= n*2-r+1; s++) {//合并的起始点int j = s + r - 1;//合并的终点//初始化m[s][j]m[s][j][0] = m[s][s][0] + m[s + 1][j][0] + sum[j] - sum[s - 1];m[s][j][1] = m[s][s][1] + m[s + 1][j][1] + sum[j] - sum[s - 1];for (int k = s + 2; k <= j; k++) {//分割点k 表示分割 [i,k-1] [k,i+1] k∈[s+1,j-1] 左边界s s+1 s+1 右边界 j-1 j jif (m[s][j][0] > m[s][k - 1][0] + m[k][j][0] + sum[j] - sum[s - 1]) {m[s][j][0] = m[s][k - 1][0] + m[k][j][0] + sum[j] - sum[s - 1];}if (m[s][j][1] < m[s][k - 1][1] + m[k][j][1] + sum[j] - sum[s - 1]) {m[s][j][1] = m[s][k - 1][1] + m[k][j][1] + sum[j] - sum[s - 1];}}}}//获得最优解int max = m[1][n][1];int min = m[1][n][0];for (int i = 2; i <= n; i++) {if (max < m[i][i + n - 1][1]) {max = m[i][i + n - 1][1];}if (min > m[i][i + n - 1][0]) {min = m[i][i + n - 1][0];}}printf("%d\n%d", min,max );return 0;}
