CF1637D题解

· · 题解

题意:

A,B 两个序列,长度为 n。 定义一个序列 a_1 , a_2 ... a_n

他的价值: 为 \sum^{n}_{i=1} \sum^{n}_{j=i+1} (a_i + a_j) ^ 2

我们可以交换 a_i,b_i 的位置。

最后求 A 的价值 + B 的价值最小。

题解思路:

先把计算价值的式子拆一下变成:

k_1 (\sum^{n}_{i=1}a_i ^ 2) + k_2 (\sum^{n}_{i=1} \sum^{n}_{j=i+1}a_i \times a_j)

已知 k_1 = n - 1 , k_2 = 2\

=(n-1)(\sum^{n}_{i=1}a_i ^ 2) + 2(\sum^{n}_{i=1} \sum^{n}_{j=i+1}a_i \times a_j)

我们可以发现,不论我们怎么改,a_i 乘方的次数是不变的。

但我们能改变 a_i \times a_j 的次数

我们定义 S_a 为他们两两想乘的结果

设一个序列为:(a_i , a_2 , a_3)

其实他就是加上了 $a_4 (a_1 + a_2 + a_3)

所以,我们就可以进行 DP 了。

因为我们只需要存 a_1 + a_2 + a_3 ... + a_nsum

就是当 $a_1 + a_2 + ... + a_i = sumA$ 并且 $b_1 + b_2 + ... + b_i = sumB$ 并且分配了前 $i$ 情况下 $S$ 的最小值。 当我们给 $a$ 序列增加一个 $x$,给 $b$ 序列增加一个 $y$ 时,$dp_{sumA,sumB}$ 其实就是加上 $x \times sumA , y \times sumB$。 实际上 $sumB$ 这一维是不需要的,因为我们有了 $dp_i$ 了,那么我们就知道了 $sumA + sumB$,我们就可以直接算出 $sumB$ 的值了。 其实 $dp_{sumA,sumB,i}$ 有很多是无意义的。 就比如 $sumA + sumB \ne (a_1 + a_2 + ... + a_n + b_1 + b_2 + ... + b_n)$,那么 $dp_{sumA , sumB , i}$ 就是无意义的。 那我们就删掉一维,因为 $sumA$ 只有一个唯一对应的一个 $sumB$,我们就把 $sumB$ 这一维压掉。 $O(n ^ 3)$ 个状态,转移时间为 $O(1)$,所以总时间复杂度为 $O(n ^ 3)$。 [【AC 记录】]() ## AC Code: ``` #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 110 , M = 20010; const long long INF = 1e15; int n; int a[N] , b[N]; long long dp[M] , tmpdp[M]; int main() { int T; cin >> T; while (T --) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) scanf("%d", &b[i]); for (int i = 0; i <= 100 * n; ++ i) dp[i] = tmpdp[i] = INF; tmpdp[0] = 0; int Sum = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j <= 100 * n; j++) { int tmp1 = j, tmp2 = Sum - j;//算出sumA,sumB if (tmpdp[j] == INF) continue; dp[j + a[i]] = min(dp[j + a[i]], tmpdp[j] + a[i] * tmp1 + b[i] * tmp2);//用了滚数组优化 dp[j + b[i]] = min(dp[j + b[i]], tmpdp[j] + a[i] * tmp2 + b[i] * tmp1); } Sum += a[i] + b[i]; for (int j = 0; j <= 100 * n; j++) { tmpdp[j] = dp[j]; dp[j] = INF; } } long long sum1 = INF, sum2 = 0; for (int i = 0; i <= 100 * n; i++) sum1 = min(sum1, tmpdp[i]);//看DP[sumA]最小的是多少 for (int i = 0; i <= n; i++) sum2 += (a[i] * a[i] + b[i] * b[i]);//就是平方的那个 printf("%lld\n", sum2 * (n - 1) + sum1 * 2);//输出答案 } return 0; } ```