【问题描述】
在操场上沿一直线排列着 n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分。允许在第一次合并前对调一次相邻两堆石子的次序。
计算在上述条件下将n堆石子合并成一堆的最小得分和初次交换的位置。
【输入文件】
输入数据共有二行,其中,第1行是石子堆数n≤100;
第2行是顺序排列的各堆石子数(≤20),每两个数之间用空格分隔。
 
【输出文件】
输出合并的最小得分。
【输入输出样例】
输入:
3
2 5 1
输出:
11
分析:
<石子归并>类型题,只是改了一个可以相邻换而已
f[i][j]:从第i堆开始到第j堆石子分成两堆的最小代价
s[i][j]:从第i堆开始到第j堆石子分成k个不同的两份(1<=k<j-i+1)的最小总代价。
则我们只需枚举每次几堆一合(2<=几<=n,1堆的已经知道),和枚举这合并的堆中的方案即可。
状态方程:

【参考程序】:
#include<fstream>
using namespace std;
int f[101][101],s[101][101],a[101];
int n;
int main()
{
        ifstream cin("stone.in");
        ofstream cout("stone.out");
        cin>>n;
        for (int i=1;i<=n;i++) cin>>a[i];
        int min=0x7FFFFFFF;
        for (int x=1;x<=n-1;x++)
        {
                int t=a[x];a[x]=a[x+1];a[x+1]=t;
                memset(f,127,sizeof(f));memset(s,127,sizeof(s));
                for (int i=1;i<=n;i++) 
                {
                        f[i][i]=a[i];s[i][i]=0;
                }
                for (int len=2;len<=n;len++)
                        for (int i=1;i<=n-len+1;i++)
                        {
                                int j=i+len-1;
                                for (int k=i;k<=j-1;k++)
                                        if (f[i][j]>f[i][k]+f[k+1][j])
                                        {
                                                f[i][j]=f[i][k]+f[k+1][j];
                                                s[i][j]=f[i][j]+s[i][k]+s[k+1][j];
                                        }
                                        else if (f[i][j]==f[i][k]+f[k+1][j])
                                                if (s[i][j]>f[i][j]+s[i][k]+s[k+1][j])
                                                        s[i][j]=f[i][j]+s[i][k]+s[k+1][j];
                        }
                if (s[1][n]<min) min=s[1][n];
                t=a[x];a[x]=a[x+1];a[x+1]=t;
        }
        cout<<min<<endl;
        return 0;
}