题目大意
有
n
n
n 张牌,写有数字
a
1
,
⋯
,
a
n
a_1,\cdots,a_n
a1,⋯,an。
每一轮操作,选择连续的三张牌,吃掉中间那张,然后把中间那张的数字加到其余两张上。
直到只剩两张牌为止。
目标是使得最后剩下的两张牌的数字和最小,输出最小的和。
2
≤
n
≤
18
,
0
≤
a
i
≤
1
0
9
2 \leq n \leq 18,~0 \leq a_i \leq 10^9
2≤n≤18, 0≤ai≤109
时限 2s
\\
\\
\\
题解
最近 atcoder 的模型怎么都这么。。。
要考虑每张牌的贡献,实际上就是想要知道每张牌被算了多少次。
然后发现算这个贼麻烦,它对左右都有贡献,还跟顺序有关。。。
所以题解又给了个好办法。
一开始,最左和最右这两张牌都是贡献
1
1
1 次的。
对于当前区间
[
l
,
r
]
[l,r]
[l,r],枚举最后被吃掉的是哪张牌,假设是
i
i
i,那么
i
i
i 这张牌的贡献次数就是
l
l
l 的次数加上
r
r
r 的次数。
设
f
l
,
r
,
x
,
y
f_{l,r,x,y}
fl,r,x,y 表示当前
[
l
,
r
]
[l,r]
[l,r] 这个区间,
l
l
l 被贡献了
x
x
x 次,
r
r
r 被贡献了
y
y
y 次,所能达到的最小代价和。于是就枚举
i
i
i 然后 dp 下去了。
分析一波复杂度。首先是状态数, l , r l,r l,r 总共是 O ( n 2 ) O(n^2) O(n2),至于 x , y x,y x,y,它与 dp 时选择哪个 i i i 无关,每往下走一层会产生两种新的状态,因此总量是 O ( 2 n ) O(2^n) O(2n) 的。因此状态数是 O ( 2 n n 2 ) O(2^nn^2) O(2nn2) 的,加上转移后时间就是 O ( 2 n n 3 ) O(2^nn^3) O(2nn3) 跑不满。
用记忆化搜索实现,代码贼短。
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
typedef pair<pair<int,int>,pair<LL,LL>> pr;
const int maxn=20;
int n;
LL a[maxn];
map<pr,LL> f;
LL dfs(int l,int r,LL x,LL y)
{
if (l+1>=r) return 0;
pr now=make_pair(make_pair(l,r),make_pair(x,y));
if (f.count(now)) return f[now];
LL re=5e18;
fo(i,l+1,r-1) re=min(re,dfs(l,i,x,x+y)+dfs(i,r,x+y,y)+a[i]*(x+y));
return f[now]=re;
}
int main()
{
scanf("%d",&n);
fo(i,1,n) scanf("%lld",&a[i]);
printf("%lld\n",dfs(1,n,1,1)+a[1]+a[n]);
}