石子合并问题--圆形版(区间dp)
在圆形操场上摆放着一行共n堆的石子。现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请编辑计算出将n堆石子合并成一堆的最小得分和将n堆石子合并成一堆的最大得分。Input输入有多组测试数据。
每组第一行为n(n<=100),表示有n堆石子,。
二行为n个用空格隔开的整数,依次表示这n堆石子的石子数量ai(0<ai<=100)
Output
每组测试数据输出有一行。输出将n堆石子合并成一堆的最小得分和将n堆石子合并成一堆的最大得分。 中间用空格分开。Sample Input3
1 2 3
Sample Output9 11
我写的是端点和区间长度式的。
环形主要处理的是,如何确定断点,使环形变成我们熟知的线性。所以采用了断环为链的手法。
将环形断开然后将其复制一条,在接上。
如下:
3
1 2 3
变成:
1 2 3 1 2 3
代码如下:
#include<iostream>
#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define IFN 0x3f3f3f
using namespace std;
int dp[210][210]; //将其复制为两个
int ff[210][210];
int num[105];
int sum[210];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>num[i];
for(int i=1;i<=n+n;i++)
sum[i]=sum[i-1]+num[(i-1)%n+1];
for(int len=1;len<n;len++)
{
for(int i=1,j=len+i;j<=n+n;i++,j++)
{
dp[i][j]=IFN;
for(int s=i;s<j;s++)
{
dp[i][j]=min(dp[i][j], dp[i][s]+dp[s+1][j]+sum[j]-sum[i-1]);
ff[i][j]=max(ff[i][j], ff[i][s]+ff[s+1][j]+sum[j]-sum[i-1]);
}
}
}
int a=IFN; int b=0;
for(int i=1;i<=n;i++)
{
a=min(a, dp[i][i+n-1]);
b=max(b, ff[i][i+n-1]);
}
cout<<a<<" "<<b<<endl;
return 0;
}