AtCoder Grand Contest 035 D


发现操作后剩下的两个数一定是\(A_{1}\)\(A_{n}\)

同理,对于一段连续区间\([l,r]\),一直进行题中操作,那么最后剩下的两个一定是\(A_{l}\)\(A_{r}\)

那么,每个数最终都会被加到\(A_{1}\)\(A_{n}\)中,而且每个数可能会加到\(A_{1},A_{n}\)中多次,那么我们不妨设\(cnt_{i}\)表示\(A_{i}\)在最终的答案里加上了多少次。

最终的答案就可以表示为\(\sum_{i=1}^{n}A_{i}cnt_{i}\)

这个也可以用来计算区间的贡献,具体的,\(dp(l,r,c1,c2)\)表示删除\((l,r)\)开区间,\(cnt(l)=c1,cnt(r)=c2\)时,最小贡献和。

转移时枚举\((l,r)\)中最后被删除的点\(i\),则\(A_{i}\)会被加到\(A_{l}\)\(A_{r}\)中各\(1\)次。根据状态定义,\(A_{l}\)会在答案中加上\(c1\)次,\(A_{r}\)会在答案中加上\(c2\)次,故\(i\)在答案中就会加上\((c1+c2)\)次,所以\(cnt(i)=c1+c2\),对答案的贡献是\(A_{i}(c1+c2)\)。区间\((l,i)\)\((i,r)\)中的数都在\(l,r,i\)之前删除了,且我们发现,\(i\)\((l,i)\)的右端点,是\((i,r)\)的左端点,这样就形成了\(dp(l,i,c1,c1+c2)\)\(dp(i,r,c1+c2,c2)\)两个子问题,就可以列出转移式如下:

\[dp_{l,r,c1,c2}=\min_{i=l+1}^{r} dp(l,i,c1,c1+c2)+dp(i,r,c1+c2,c2)+(c1+c2)A_{i} \]

考虑\(n\)只有\(18\),这个转移可以暴力搜索。

#include
using namespace std;
typedef long long ll;
const ll inf=1000000000000000000ll;
ll n,a[25];
ll dfs(int l,int r,ll cl,ll cr) {
	if(l>r) return 0ll;
	ll ans=inf;
	for(int i=l;i<=r;i++)
		ans=min(ans,dfs(l,i-1,cl,cl+cr)+dfs(i+1,r,cl+cr,cr)+a[i]*(cl+cr));
	return ans;
}
int main(int argc,char **argv) {
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	cout<