Yet Another Minimization Problem(数学、DP)
题意
给定\(a, b\)两个长度为\(n\)的数组。你可以进行任意次操作,每次操作选中一个\(i\),交换\(a_i\)和\(b_i\)。
求\(\sum\limits_{i = 1}^n \sum\limits_{j = i + 1}^n (a_i + a_j)^2 + \sum\limits_{i = 1}^n \sum\limits_{j = i + 1}^n (b_i + b_j)^2\)的最小值。
数据范围
\(T\)组询问,\(1 \leq T \leq 100\)
\(1 \leq n \leq 100\)
\(1 \leq a_i, b_i \leq 100\)
思路
首先对式子进行化简,
\[\begin{aligned}
\sum\limits_{i = 1}^n \sum\limits_{j = i + 1}^n (a_i + a_j)^2 &= \sum\limits_{i = 1}^n \sum\limits_{j = i + 1}^n (a_i^2 + a_j^2 + 2a_ia_j) \\
&= (n - 1)\sum\limits_{i = 1}^n a_i^2 + \sum\limits_{i = 1}^n \sum\limits_{j = 1}^n a_ia_j - \sum\limits_{i = 1}^n a_i^2\\
&= (n - 2)\sum\limits_{i = 1}^n a_i^2 + (\sum\limits_{i = 1}^n a_i)^2 \\
\end{aligned}
\]
因此,最小化原式等价于最小化\((\sum\limits_{i = 1}^n a_i)^2 + (\sum\limits_{i = 1}^n b_i)^2\)。
对上面的式子继续化简。令\(s = \sum\limits_{i = 1}^n a_i + \sum\limits_{i = 1}^n b_i\),\(t = \sum\limits_{i = 1}^n a_i\),则:
\[(\sum\limits_{i = 1}^n a_i)^2 + (\sum\limits_{i = 1}^n b_i)^2 = t^2 + (s - t)^2
\]
因此,我们只需要考虑\(t\)可能的取值,即:通过若干次操作,\(\sum\limits_{i=1}^n a_i\)可能的取值有哪些。
考虑DP,令\(f_{i,j}\)表示前\(i\)项,通过若干次操作,是否能得到\(j\)。转移方程为:\(f_{i, j} = f_{i - 1, j - a_i} | f_{i - 1, j - b_i}\)
然后枚举所有可能取值即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 110, M = 2010;
int n;
int a[N], b[N];
bool f[N][M];
int main()
{
int T;
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
int sum = 0, mul = 0;
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
sum += a[i];
mul += a[i] * a[i];
}
for(int i = 1; i <= n; i ++) {
scanf("%d", &b[i]);
sum += b[i];
mul += b[i] * b[i];
}
memset(f, 0, sizeof f);
f[0][0] = 1;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= sum; j ++) {
if(j >= a[i] && f[i - 1][j - a[i]]) f[i][j] = true;
if(j >= b[i] && f[i - 1][j - b[i]]) f[i][j] = true;
}
}
int tmp = (n - 2) * mul;
int ans = 1e9;
for(int i = 0; i <= sum; i ++) {
if(f[n][i]) {
ans = min(ans, i * i + (sum - i) * (sum - i) + tmp);
}
}
printf("%d\n", ans);
}
return 0;
}