您的位置:首页 > 其它

石子合并(NOI1995)

2017-06-01 20:33 337 查看
题目描述

在操场上沿一直线排列着 n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分。允许在第一次合并前对调一次相邻两堆石子的次序。

计算在上述条件下将n堆石子合并成一堆的最小得分和初次交换的位置。

输入

输入数据共有二行,其中,第1行是石子堆数n≤100;

第2行是顺序排列的各堆石子数(≤20),每两个数之间用空格分隔。

输出

输出合并的最小得分。

样例输入

3

2 5 1

样例输出

11

我们令f[i][j]为i~j合并后的最小得分

我们可以得到DP转移方程f[i][j]=min(f[i][k−1]+f[k][j]+∑ija[i](a[i]为各堆石子数))

题目说可以交换任意两堆石子,所以我们可以枚举,并且a[i]的和我们可以维护前缀和。

此题我们也可以用平行四边形优化,不过不用优化依旧能AC此题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
long long sum[210],a[210],f[210][210];
int main()
{
scanf("%d",&n);
sum[0]=0;
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
long long ans=1<<30;
for (int T=1;T<n;T++)
{
swap(a[T],a[T+1]);
memset(f,127,sizeof(f));
sum[0]=0;
for (int i=1;i<=n;i++) f[i][i]=0,sum[i]=sum[i-1]+a[i];
for (int i=1;i<=n;i++)
for (int j=1;j<=n-i;j++)
for (int k=j+1;k<=j+i;k++)
f[j][j+i]=min(f[j][j+i],f[j][k-1]+f[k][j+i]+sum[j+i]-sum[j-1]);
ans=min(ans,f[1]
);
swap(a[T],a[T+1]);
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: