#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 110;
const int inf = 0x3f3f3f3f;
// 题目链接: http://acm.fzu.edu.cn/problem.php?pid=1319
/*
经典的石子合并问题,存在O(n^3)的状态转移方程
dp[i][j]表示i--->j的石子合并成一堆需要的最小花费,则
dp[i][j] = min(dp[i][k] + dp[k+1][j]) + sum(i, j) (i<=k<j)
由于本题存在一种操作是可以交换一个相邻堆得石子,这样再加上枚举操作
总的复杂度便成为O(n^4),因此需要优化状态转移方程。
状态转移方程的优化见:http://it.dgzx.net/drkt/oszt/zltk/yxlw/dongtai3.htm
令s[i, j] = max(k | dp[i][j] = dp[i][k] + dp[k+1][j] + sum(i,j))
这样对于dp[i][j]的最优分割点k满足s[i][j-1]<=k<=s[i+1][j]
*/
int a[maxn], sum[maxn], dp[maxn][maxn], s[maxn][maxn];
int n;
int solve() {
sum[0] = 0;
for(int i = 1; i <= n; ++i) {
dp[i][i] = 0;
s[i][i] = i;
sum[i] = sum[i-1] + a[i];
}
for(int l = 1; l < n; ++l) {
for(int i = 1; i + l <= n; ++i) {
int j = i + l;
dp[i][j] = inf;
for(int k = s[i][j-1]; k <= s[i+1][j]; ++k) {
if(dp[i][j] >= dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]) {
dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
s[i][j] = k;
}
}
}
}
return dp[1][n];
}
inline void swap(int &a, int &b) {
int t = a;
a = b;
b = t;
return ;
}
int main() {
while(scanf("%d", &n) != EOF) {
sum[0] = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
sum[i] = sum[i-1] + a[i];
}
int ans = solve();
int t_ans = ans;
int pos = 0;
for(int i = 1; i < n; ++i) {
swap(a[i], a[i+1]);
int t = solve();
if(ans >= t) {
ans = t;
pos = i;
}
swap(a[i], a[i+1]);
}
printf("%d\n", ans);
if(t_ans == ans) {
printf("(0,1)\n");
} else {
printf("(%d,%d)\n", pos, pos + 1);
}
}
return 0;
}
10-11