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][i1][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;
}
posted @ 2021-11-02 07:37  lahlah  阅读(38)  评论(0编辑  收藏  举报