[dp记录][AGC035D] Add and Remove
牛逼题。
首先 \(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;
}

浙公网安备 33010602011771号