AGC035D

AGC035D Add and Remove

题意

给出$n$个数,每次删除一个不在两端的数,然后把它的权值加到相邻的两个数上。

问操作$n-2$次后,所剩的两数之和的最小值

\(n\le18\)

题解

暴力存储每一个数的状态肯定不行。

考虑计算每一个数被计算了多少次。

可以发现$1$和$n$一定只被计算了1次

最后一个被消除掉的数应只被计算$2$次

可以发现,如果左端点被计算$x$次,右端点被计算$y$次

那么左右端点之间最后一个被消除的数被计算了$x+y$次

然后就可以开始记忆化搜索了

注意全部都用$map$存储状态会$TLE$,需要把其中一部分用数组存储

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int sz=20;
int n;
struct node{
	int l,r,fl,fr;
	const bool operator<(const node &p)const{
		if(l!=p.l) return l<p.l;
		if(r!=p.r) return r<p.r;
		if(fl!=p.fl) return fl<p.fl;
		if(fr!=p.fr) return fr<p.fr;
	}
};
ll a[sz];
ll g[sz][sz][611][611];
map<node,ll>dp;
ll f(int l,int r,int fl,int fr){
	if(l+1==r) return 0;
	if(fl<611&&fr<611){
		if(g[l][r][fl][fr]) return g[l][r][fl][fr];
	}
	else if(dp[(node){l,r,fl,fr}]) return dp[(node){l,r,fl,fr}];
	ll ret=1e18;
	ll sum=fl+fr;
	for(int i=l+1;i<=r-1;i++){
		ret=min(ret,sum*a[i]+f(l,i,fl,sum)+f(i,r,sum,fr));
	}
	if(fl<611&&fr<611) return g[l][r][fl][fr]=ret;
	else return dp[(node){l,r,fl,fr}]=ret;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	printf("%lld\n",a[1]+f(1,n,1,1)+a[n]);
} 
上一篇:【题解】P3980 [NOI2008]志愿者招募(费用流求线性规划)


下一篇:FL Studio中使用自动剪辑包络方法(上)