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;
}
posted @ 2022-04-20 11:53  pbc的成长之路  阅读(57)  评论(0编辑  收藏  举报