题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输入样例#1: 复制
4
4 5 9 4
输出样例#1: 复制
43
54
区间dp的关键:
1.有环将环拆成链;
2.单区间可能有值,可能没值,视情况而定
3.拆区间
4.区间合并
可以观察到区间DP的时间复杂度为O(n^3)
所以发现数据<=1000的话那就是区间dp了
#include <cstdio>
#include <iostream>
using namespace std;
#define MAXN 510
#define INF 0x3f3f3f3f
int a[MAXN<<1],sum[MAXN<<1],dp1[MAXN][MAXN<<1],dp2[MAXN][MAXN<<1];
int main()
{
int n;
int i,k;
int right,left;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
for(i=1;i<=2*n;i++)
{
sum[i]=sum[i-1]+a[i];
}
for(right=2;right<=2*n;right++) //right向左的区间更新
{
for(left=right-1;right-left<n&&left>=1;left--)//考虑到有right+1,所以right<2n
{
dp1[left][right]=INF;
dp2[left][right]=-INF;
for(k=left;k<right;k++)
{
dp1[left][right]=min(dp1[left][right],dp1[left][k]+dp1[k+1][right]+sum[right]-sum[left-1]);//区间合并就是将这一整个区间的数取和
dp2[left][right]=max(dp2[left][right],dp2[left][k]+dp2[k+1][right]+sum[right]-sum[left-1]);
}
}
}
int min_ans=INF,max_ans=-INF;
for(i=1;i<=n;++i)
{
min_ans=min(min_ans,dp1[i][i+n-1]);
max_ans=max(max_ans,dp2[i][i+n-1]);
}
printf("%d\n%d\n",min_ans,max_ans);
return 0;
}
以len的区间dp写法
#include<cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
#define MAXN 110
#define INF 0x3f3f3f3f
int main()
{
int n;
int i,k;
int len;
int left,right;
int dp[MAXN][MAXN];
int a[MAXN];
int sum[MAXN];
while(scanf("%d",&n),n!=0)
{
memset(dp,0,sizeof(dp)); //长度为1的dp值是0的。初始为0,在循环内设为INF
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
sum[0]=0;
for(i=1;i<=2*n;i++)
sum[i]=sum[i-1]+a[i];
for(len=2;len<=n;len++)
{
for(left=1;left<=2*n+1-len;left++) //left要拓展到2n的,因为区间改变成2n了呀
{
right=left+len-1;
dp[left][right]=INF;
for(k=left;k<right;k++) //k从left到right-1
dp[left][right]=min(dp[left][right],dp[left][k]+dp[k+1][right]+sum[right]-sum[left-1]);
}
}
int ans=INF;
for(i=1;i<=n;i++)
{
ans=min(ans,dp[i][i+n-1]);
}
printf("%d\n",ans);
}
return 0;
}