[dp记录][AGC035D] Add and Remove

\(\texttt{link}\)

牛逼题。

首先 \(a_1,a_n\) 对答案的贡献是确定的,可以不考虑。

\(b_i\) 表示丢进第 \(i\) 个位置的数的总和,删除 \(i\) 可以看作是将 \(b_i + a_i\) 丢进相邻的两个数的 \(b\) 里。

假设最后一个删除的是 \(i\),那么对于 \((1,i)\),有些数丢到了 \(b_1\) 里,有些数丢到 \(b_i\) 里,\((i,n)\) 同理,那么删去 \(a_i\) 后对答案的贡献为 \(2 * b_i + a_i\)

再考虑倒数第二个删除的,记作 \(j\)

\(\bullet\)\(j \in (1,i)\),那么它往左丢对答案的贡献为 \(b_i + a_i\),往右丢对答案的贡献为 \(2 * (b_i + a_i)\);

\(\bullet\)\(j \in (i,n)\),那么它往左丢对答案的贡献为 \(2 * (b_i + a_i)\),往右丢对答案的贡献为 \(b_i + a_i\)

可以发现,每个数对左右的贡献并不一定是相等的,于是记 \(dp(l,r,gl,gr)\) 表示删掉 \([l,r]\) 的所有数,往左丢对答案的贡献系数为 \(gl\),往右丢贡献系数为 \(gr\), 对答案的最小贡献,则有转移:

\[dp(l,r,gl,gr) = \min\limits_{i=l}^r dp(l,i-1,gl,gl+gr) + dp(i+1,r,gl+gr,gr) + a_i * (gl + gr) \]

答案即为 \(a_1 + a_n + dp(2,n-1,1,1)\)

\(\texttt{Code:}\)

#include <bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;
const int N = 16;
int n, a[N];
map<pair<int, int>, ll> dp[N][N];
ll solve(int l, int r, int gl, int gr) {
	if (l > r) return 0;
	if (dp[l][r].find(mp(gl, gr)) != dp[l][r].end()) return dp[l][r][mp(gl, gr)];
	ll &res = dp[l][r][mp(gl, gr)];
	res = 1e18;
	for (int i = l; i <= r; i++)
		res = min(res, solve(l, i - 1, gl, gl + gr) + solve(i + 1, r, gl + gr, gr) + 1ll * (gl + gr) * a[i]);
	return res;
}
int main() {
	int ans;
	scanf("%d%d", &n, &ans);
	for (int i = 0; i < n - 2; i++) scanf("%d", &a[i]);
	int x; scanf("%d", &x); ans += x;
	printf("%lld", ans + solve(0, n - 3, 1, 1));
	return 0;
}
posted @ 2021-11-06 11:40  klii  阅读(39)  评论(0)    收藏  举报