AT5141 [AGC035D] Add and Remove
https://www.luogu.com.cn/problem/AT5141
NB区间DP题
直接DP不太好做,我们考虑计算每个数对答案的贡献系数是多少(最后一个数会被加若干遍)
a
[
1
]
,
a
[
n
]
a[1],a[n]
a[1],a[n]显然只会被加1次,
我们考虑枚举最后一个删除的数,那个数显然会被加两次
画出来一个图
容易发现,最后删的这个点的贡献系数为两边的贡献系数的和
所以就可以设出状态了 f [ l ] [ r ] [ c l ] [ c r ] f[l][r][cl][cr] f[l][r][cl][cr]表示区间 [ l , r ] [l,r] [l,r]左边的贡献系数为 c l cl cl,右边的为 c r cr cr
f
[
l
]
[
r
]
[
c
l
]
[
c
r
]
=
∑
f
[
l
]
[
i
−
1
]
[
c
l
]
[
c
l
+
c
r
]
+
f
[
i
+
1
]
[
r
]
[
c
l
+
c
r
]
[
c
r
]
+
a
[
i
]
∗
(
c
l
+
c
r
)
f[l][r][cl][cr]=\sum f[l][i-1][cl][cl+cr]+f[i+1][r][cl+cr][cr]+a[i]*(cl+cr)
f[l][r][cl][cr]=∑f[l][i−1][cl][cl+cr]+f[i+1][r][cl+cr][cr]+a[i]∗(cl+cr)
考虑树的形态,状态数并不是很多(类似斐波那契数列),
O
(
n
2
2
n
)
O(n^2 2^n)
O(n22n)直接暴力搜索即可
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[25];
int n;
ll dfs(int l, int r, int cl, int cr) {
if(l > r) return 0;
ll ret = 1e18;
for(int i = l; i <= r; i ++) ret = min(ret, dfs(l, i - 1, cl, cl + cr) + dfs(i + 1, r, cl + cr, cr) + a[i] * (cl + cr));
return ret;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
printf("%lld", a[1] + a[n] + dfs(2, n - 1, 1, 1));
return 0;
}