【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\n",ans);
    return 0;
}
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号