AGC035D Add and Remove

题目大意

有一个长度为$n$的序列$\{a_i\}$,每次可以选择连续的$3$个数,把中间那个数加到左右两个数上后删除中间那个数。

求最后剩下的两个数的最小值。 $n \leq 18$

分析

我们发现最后的结果肯定是每一个$a_i$乘上一个系数的和,我们考虑倒着$\texttt{dp}$

设$\texttt{f[l][r][x][y]}$表示当前区间的左右端点分别是$l, \; r$,$[l, r]$之间的部分已被删除,$a_l$的系数为$x$,$a_r$的系数为$y$,对整个序列贡献的答案。

那么我们模仿区间$\texttt{dp}$的形式,枚举一个中间点$\texttt{mid} \in (l, r)$则$f[l][r][x][y] = \min\{f[l][mid][x][x+y]+f[mid][r][x+y][y]+a[mid] \times (x+y)\}$

由于$n$不大,直接搜索即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>

#define R register
#define ll long long
#define sum(a, b, mod) (((a) + (b)) % mod)

ll n, a[50];

ll f(ll l, ll r, ll x, ll y)
{
ll ans = 1e18;
if(r - l <= 1) return 0;
for(int i = l + 1; i <= r - 1; i++)
ans = std::min((f(l, i, x, x + y) + f(i, r, x + y, y) + a[i] * (x + y)), ans);
return ans;
}

int main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
printf("%lld\n", f(1, n, 1, 1) + a[1] + a[n]);
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×