原题链接
题目大意
在一个圆形操场的四周摆放
N
N
N堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将
N
N
N堆石子合并成
1
1
1堆的最小得分和最大得分。
输入格式
数据的第
1
1
1行是正整数
N
N
N,表示有
N
N
N堆石子。
第
2
2
2行有
N
N
N个整数,第
i
i
i个整数
a
i
a_i
ai表示第
i
i
i堆石子的个数。
输出格式
输出共 2 2 2行,第 1 1 1行为最小得分,第 2 2 2行为最大得分。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input
4
4 5 9 4
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
43
54
H
i
n
t
&
E
x
p
l
a
i
n
\mathbf{Hint\&Explain}
Hint&Explain
最小价值:
把4
和4
合并,得分8
,石子变成8 5 9
。
把8
和5
合并,得分13
,石子变成13 9
。
把13
和9
合并,得分22
,石子变成22
。
总得分8+13+22=43
。
最大价值:
把5
和9
合并,得分14
,石子变成4 14 4
。
把4
和14
合并,得分18
,石子变成18 4
。
把18
和4
合并,得分22
,石子变成22
。
总得分14+18+22=54
。
数据范围
对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 100 , 1 < a i < 20 1≤n≤100,1<a_i<20 1≤n≤100,1<ai<20。
解题思路
此题为区间dp。
先从普通版本来入手。
假如说这些石头是排成一列的,那么我们就可以枚举其中的一段区间求答案。
设
f
i
,
j
f_{i,j}
fi,j为合并第
i
i
i堆石子到第
j
j
j堆石子的最小得分,
s
u
m
i
,
j
sum_{i,j}
sumi,j为第
i
i
i堆石子到第
j
j
j堆石子的石子个数,由于
[
i
,
j
]
[i,j]
[i,j]这一段区间是由两个小区间合并得来的,则我们可以在这个区间内枚举一个断点,把这个区间分成两份。按照题目中的说法,我们就可以得出状态转移方程:
f
i
,
j
=
{
0
i
=
j
f
i
,
k
+
f
k
+
1
,
j
+
s
u
m
i
,
j
i
<
j
,
i
≤
k
<
j
f_{i,j}=\begin{cases} 0&i=j \\ f_{i,k}+f_{k+1,j}+sum_{i,j}&i<j,i\le k<j \end{cases}
fi,j={0fi,k+fk+1,j+sumi,ji=ji<j,i≤k<j
✒
注意
由于大区间是由小区间合并得来的,所以在枚举区间的
时候要从小区间枚举到大区间。
{ \def\fcol{#448AFF} \def\scol{#ECF3FF} \def\sidelength{50pt} \def\titlehigh{30pt} \def\titlewordhigh{37pt} \color{\fcol}{\rule[0pt]{2pt}{\sidelength}} \color{\scol}{\rule[\titlehigh]{200pt}{20pt}} \kern{-200pt} \color{#FFFFFF}{\rule[0pt]{200pt}{\titlehigh}} \color{\fcol}{\raisebox{\titlewordhigh}{\kern{-195pt}{\footnotesize\bf ✒ 注意}}} \color{black} % 在raisebox{ /here/ }{}修改文字高度,底线为7pt,依此减少10pt {\raisebox{17pt}{\kern{-195pt}{\footnotesize\bf 由于大区间是由小区间合并得来的,所以在枚举区间的}}} {\raisebox{7pt}{\kern{-195pt}{\footnotesize\bf 时候要从小区间枚举到大区间。}}} }
✒ 注意由于大区间是由小区间合并得来的,所以在枚举区间的时候要从小区间枚举到大区间。
上代码
#include<bits/stdc++.h>
using namespace std;
#define fu(i,st,ed) for(int i=st; i<=ed; i++)
#define fd(i,st,ed) for(int i=st; i>=ed; i--)
int f1[210][210];
int f2[210][210];
int sum[210];
int a[210];
int i,j,l;
int n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
/* Code */
cin>>n;
fu(i,1,n)
cin>>a[i],a[i+n]=a[i];
fu(i,1,2*n)
sum[i]=sum[i-1]+a[i];
fu(i,2,n)
fu(l,1,2*n-i+1)
{
int r=l+i-1;
f1[l][r]=0;
f2[l][r]=2147483647;
fu(j,l,r-1)
{
f1[l][r]=max(f1[l][r],f1[l][j]+f1[j+1][r]+sum[r]-sum[l-1]);
f2[l][r]=min(f2[l][r],f2[l][j]+f2[j+1][r]+sum[r]-sum[l-1]);
}
}
int tar1=0,tar2=2147483647;
fu(l,1,n+1)
{
int r=l+n-1;
tar1=max(tar1,f1[l][r]);
tar2=min(tar2,f2[l][r]);
}
cout<<tar2<<endl<<tar1<<endl;
return 0;
}
完美切题 ∼ \sim ∼