1021 石子归并
- 1 秒
- 131,072 KB
N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
收起
输入
第1行:N(2 <= N <= 100)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
输出
输出最小合并代价
输入样例
4
1
2
3
4
输出样例
19
解析:
建立最优值递归式:
dp[i][j]代表第i堆到第j堆合并的最小花费,sum(j~i-1)代表第i堆到第j堆的石子数目和
初始化dp[i][j]=inf,dp[i][i]=0;
dp[i][ends]=min(dp[i][ends],dp[i][j]+dp[j+1][ends]+sum(ends-i+1));
ac:
#include<bits/stdc++.h>
#define inf 0x3f3f3f
#define MAXN 105
using namespace std;
int a[MAXN]={0};//石头
int sum[MAXN]={0};
int dp[MAXN][MAXN]={0};
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=inf;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
dp[i][i]=0;
}
for(int len=1;len<n;len++)//枚举长度
{
for(int i=1;i+len<=n;i++)//枚举起点
{
int j=i+len;
for(int j=i;j<j;j++)//枚举分割点
{
dp[i][j]=min(dp[i][j],dp[i][j]+dp[j+1][j]+sum[j]-sum[i-1]);
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}
环形石子合并
题意:
石子合并变成环形的,求合并最小代价
解析:
区间dp+四边形优化,递推式和线性的一样,这题复杂度要求为O(N^2),要用四边形优化
利用四边形不等式优化,限制k(i,j)的取值范围在k∈(i,j-1)~k(i+1,j).之间将复杂度O(N^3)优化到O(N^2)
四边形优化学习:https://blog.csdn.net/u014800748/article/details/45750737
ac:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x7f7f7f7f
#define MAXN 2005
using namespace std;
ll dp[MAXN][MAXN],s[MAXN][MAXN];
ll sum[MAXN],a[MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(dp,inf,sizeof(dp));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
for(int i=1;i<=2*n;i++)
{
sum[i]=sum[i-1]+a[i];
dp[i][i]=0;
s[i][i]=i;
}
for(int len=1;len<=n;len++)
{
for(int i=1;i+len<=2*n;i++)
{
int j=i+len;
for(int k=s[i][j-1];k<=s[i+1][j];k++)
{
ll temp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
if(dp[i][j]>=temp)
{
dp[i][j]=temp;
s[i][j]=k;
}
}
}
}
ll ans=inf;
for(int i=1;i<=n;i++)
ans=min(ans,dp[i][i+n-1]);
printf("%lld\n",ans);
}
return 0;
}