【a903】石子归并

Time Limit: 10 second
Memory Limit: 2 MB

问题描述
在一个圆形操场的四周摆放着N堆石子(N<= 100),现要将石子有次序地合并成一堆.规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.编一程序,由文件读入堆栈数N及每堆栈的石子数(<=20).
选择一种合并石子的方案,使用权得做N-1次合并,得分的总和最小。

Input

第一行为石子堆数N; 第二行为每堆的石子数,每两个数之间用一个空格分隔.
Output

输出总和最小

Sample Input

4
4 5 9 4
Sample Output

43

【题目链接】:http://noi.qz5z.com/viewtask.asp?id=a903

【题解】

线性的话可以写出
f[i][j]表示i..j这些石子合在一起的最小代价;
换成环的话;可以改成
f[i][j]表示从第i个石子开始i..i+j-1合在一起需要的最小代价;因为变成环了;所以把a[1..n]复制到a[n+1..2n];就好
f[i][j] = min(f[i][j],f[i][k]+f[i+k][j-k]+s[i+j-1]-s[i-1]);
当然s[i]也要多复制一遍;
同时i要循环到2*n才行;当中如果出现i+j>=2*n就不继续了;否则那些f[i][j]i大于n的时候,j如果是合法的继续搞;(即i+j<=2*n);这样f[i+k][j-k]才能在用之前被算出来;

【完整代码】

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAXN = 110;
const int INF = 0x3f3f3f3f;

int n,a[MAXN],s[MAXN];
int f[MAXN*2][MAXN];

int main()
{
    //freopen("F:\rush.txt","r",stdin);
    scanf("%d",&n);
    for (int i = 1;i <= n;i++)
        scanf("%d",&a[i]);
    for (int i = n+1;i <= 2*n;i++)
        a[i] = a[i-n];
    for (int i = 1;i <= 2*n;i++)
        s[i] = s[i-1]+a[i];
    memset(f,INF,sizeof(f));
    for (int i = 1;i <= 2*n;i++)
        f[i][1] = 0;
    for (int l = 2;l <= n;l++)
        for (int i = 1;i <= 2*n && i+l<=2*n;i++)
            for (int k = 1;k<=l-1;k++)
                f[i][l] = min(f[i][l],f[i][k]+f[i+k][l-k]+s[i+l-1]-s[i-1]);
    int ans = f[1][n];
    for (int i = 2;i <= n;i++)
        ans = min(ans,f[i][n]);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/AWCXV/p/7626953.html