#include<bits/stdc++.h>
#include <iostream>
using namespace std;
//区间动态规划
//P1880 [NOI1995] 石子合并
//fx[i][j]表示区间i到j的最大合并值
//推导:细分从一堆开始
//边界:f(i,i) = 0
//f(1,2) = f(1,1) +f(2,2)+ sum(1,2) sum(1,2)表示从1到2的区间和
//每次区间石子的合并最后都要加上此区间的总和这个与石子的合并顺序无关
//环装开2倍数组
int m_max[205][205],m_min[205][205];//存最大最小值的数组
int sum[205];//前缀和
int a[205];//石子堆
int N;
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
a[i+N]=a[i];//取相同的一段接到后面相当于环
//初始化 f(i,i) = 0
m_min[i][i]=0;
m_max[i][i]=0;
}
//求前缀和
sum[0]=0;
for(int i=1;i<=2*N;i++)
{
sum[i]=sum[i-1]+a[i];
}
//动态规划
//采用枚举区间长度的方法
//因为区间长度为1的为0,边界:f(i,i) = 0,直接从2开始枚举
for(int len=2;len<=N;len++)
{
for(int left=1;left+len-1<=2*N;left++)//区间的左端点
{
int right=left+len-1;//区间右端点
m_min[left][right]=0x3f3f3f3f;//假设最小值很大
m_max[left][right]=0;//假设最大值
//分界线k
for(int k=left;k<right;k++)//采用左闭右开 k+1<right
{
m_max[left][right]=max(m_max[left][right],m_max[left][k]+m_max[k+1][right]);
m_min[left][right]=min(m_min[left][right],m_min[left][k]+m_min[k+1][right]);
}
//每次合并固定要不变要加上的部分区间left到right的石子总和
m_max[left][right]+=sum[right]-sum[left-1];
m_min[left][right]+=sum[right]-sum[left-1];
}
}
//最后寻找长度为N的区间的最大最小值
int mi=0x3f3f3f3f;
int ma=-1;
for(int i=1;i<=N;i++)
{
if(m_max[i][i+N-1]>ma) ma=m_max[i][i+N-1];
if(m_min[i][i+N-1]<mi) mi=m_min[i][i+N-1];
}
printf("%d\n%d",mi,ma);
return 0;
}
区间动态规划 P1880 [NOI1995] 石子合并
最新推荐文章于 2024-06-01 18:28:10 发布