[2020.11.13]AGC035D - Add and Remove

题意

给出一个长度为\(n\)的序列\(A\),每次可以做如下操作:

任取一个整数\(w\),满足\(2\le w\le\)当前序列长度 ,令\(A_{w-1}\)\(A_{w+1}\)加上\(A_w\),然后删去\(a_w\)

重复上述操作直到序列长度为\(2\),求最终\(A_1+A_2\)的最小值。

题解

不妨倒着考虑整个过程。每次考虑当前最后一个被删除的数并加入。

\(V_x\)表示\(A_x\)向最终总和的贡献次数,一开始\(V_1=V_n=1\),那么设当\(x\)被加入时,它左边第一个已经被加入的数是\(L\),右边是\(R\),那么\(V_x=V_L+V_R\)

于是,我们记\(dp_{l,r,x,y}\)表示\(l\)\(r\)已经加入,\([l+1,r-1]\)都还没加入,\(V_l=x\)\(V_r=y\) 时的最小贡献。

那么

\[dp_{l,r,x,y}=\min_{m=l+1}^{r-1}dp_{l,m,x,x+y}+dp_{m,r,x+y,y}+A_m\times(x+y) \]

注意到状态中的\(x,y\)都是\(O(2^n)\)级别,因此可以使用\(map\)和记忆化搜索实现。

code:

#include<bits/stdc++.h>
#define ci const int&
using namespace std;
struct sta{
	int l,r,x,y;
	bool operator<(const sta&t)const{
		return l==t.l?(r==t.r?(x==t.x?y<t.y:x<t.x):r<t.r):l<t.l;
	}
};
int n,a[20];
map<sta,long long>dp;
long long DP(ci l,ci r,ci x,ci y){
	if(l>=r-1)return 0;
	if(dp.count((sta){l,r,x,y}))return dp[(sta){l,r,x,y}];
	long long tmp=1e18;
	for(int i=l+1;i<r;++i)tmp=min(tmp,DP(l,i,x,x+y)+DP(i,r,x+y,y)+1ll*a[i]*(x+y));
	return dp[(sta){l,r,x,y}]=tmp;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	printf("%lld",DP(1,n,1,1)+a[1]+a[n]);
	return 0;
}
posted @ 2020-11-13 19:55  xryjr233  阅读(113)  评论(0编辑  收藏  举报