石子合并的圆形版和直线版差不多,关键是怎么把圆变成直线来处理。
以最小值为例:
1.先初始化mi[m][m];
2.枚举区间长度len,当区间长度为1时,相应的mi[len][i] = 0;
3.枚举起始位置i;
4.枚举插入位置,mi[j][i]代表从i开始,长度为j的最小得分。
5.将圆变成直线的方法是,当i+j大于n时,第二段开始的位置就要重新计算,
设pos为第二段开始的坐标,pos =int pos = (i+j)%n if(pos == 0) pos = n;
dp[i][j] = min(dp[i][j],dp[j][i]+dp[len-j][pos]+getsum[j][i]);
6.getsum(j,i)计算的是从i开始,长度为j的所有石子数量的和,如果i+len-1<=n
getsum(j,i) = sum[j]-sum[i-1];
如果大于
就要算出从第i堆到第n堆的石子数量,和第一队到第pos堆的石子数量
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int inf = 100000000;
const int m = 105;
int mi[m][m];
int ma[m][m];
int a[m];
int sum[m];
int mis,mas,n;
int getsum(int len,int i)
{
if((i+len-1)<=n) return sum[i+len-1]-sum[i-1];
else
{
return (sum[n]-sum[i-1]+sum[(i+len-1)%n]);
}
}
void solve_mi()
{
for(int i = 0 ; i <= n ; i++)
for(int j = 0 ; j <= n ; j++)
{
mi[i][j] = inf;//初始化最小最大值
}
for(int len = 1 ; len <= n ;len++)//代表区间长度,一定从小区间到大区间
{
for(int i = 1 ; i <= n ; i++)//起始点位置,mi[i][len]代表从i开始长度为n的区间
{
if(len == 1)//长度为1初始值为0
{
mi[len][i] = 0;
continue;
}
for(int j = 1 ; j < len ; j++)//j代表插入位置,mi[j][i]代表从i开始长度为j的区间,原来长度为len,通过j分割为长度为j和len-j两个区间
{
int pos = (i+j)%n;
if(pos == 0) pos = n;
int k = getsum(len,i);
mi[len][i] = min(mi[len][i],mi[j][i]+mi[len-j][pos]+k);
}
}
}
mis = inf;
for(int i = 1 ; i <= n ; i++)
mis = min(mis,mi[n][i]);
}
void solve_ma()
{
for(int i = 0 ; i <= n ; i++)
for(int j = 0 ; j <= n ; j++)
{
ma[i][j] = 0;
}
for(int len = 1 ; len <= n ;len++)
{
for(int i = 1 ; i <= n ; i++)
{
if(len == 1)
{
ma[len][i] = 0;
continue;
}
for(int j = 1 ; j < len ; j++)
{
int pos = (i+j)%n;
if(pos == 0) pos = n;
int k = getsum(len,i);
ma[len][i] = max(ma[len][i],ma[j][i]+ma[len-j][pos]+k);
}
}
}
mas = 0;
for(int i = 1 ; i <= n ; i++)
mas = max(mas,ma[n][i]);
}
int main()
{
while(~scanf("%d",&n))
{
memset(sum,0,sizeof(sum));
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&a[i]);
sum[i]= sum[i-1]+a[i];
}
solve_mi();
solve_ma();
cout<<mis<<" "<<mas<<endl;
}
return 0;
}