【AtCoder Grand 035D】Add and Remove 题解

题目大意

  有 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 2n18, 0ai109
  时限 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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值