### 文章目录
- [@[toc]](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [一、题目](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [1、题目描述](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [2、输入输出](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [2.1输入](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [2.2输出](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [3、原题链接](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [二、解题报告](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [1、思路分析](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [2、复杂度](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
- [3、代码详解](#文章目录 @[toc] 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解)
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
考虑一个经典贪心问题:
两个长度为n的正整数数组a,b,满足 Σa = Σb,允许每次选定i,使得a[i] 减1,然后让a[i + 1] 或 a[i - 1] 加1,最少需要多少次可以使得 a = b?
答案为: ∑ i = 0 n − 1 ∣ ∑ j = 0 i a j − ∑ j = 0 i b j ∣ \sum_{i=0}^{n-1} |\sum_{j=0}^{i}a_j-\sum_{j=0}^{i}b_j| ∑i=0n−1∣∑j=0iaj−∑j=0ibj∣
即,两个数组的前缀和数组的 差的绝对值 之和,只需证明下界,并且可以取到即可:
先证明下界:
记 s a [ i ] = ∑ j = 0 i a i , s b [ i ] = ∑ j = 0 i b i sa[i] = \sum_{j=0}^{i}a_i,sb[i] = \sum_{j=0}^{i}b_i sa[i]=∑j=0iai,sb[i]=∑j=0ibi
那么对于下标 i:
- 如果 sa[i] > sb[i],说明 [0...i] 的数至少需要向[i+1...n]运输 sa[i] - sb[i]
- 否则,说明 [i+1...n] 的数至少需要向 [0...i] 运输 sb[i] - sa[i]
因此,答案的下界是 ∑ i = 0 n − 1 ∣ s a [ i ] − s b [ i ] ∣ \sum_{i=0}^{n-1} |sa[i] - sb[i]| ∑i=0n−1∣sa[i]−sb[i]∣
在证明可以构造方案使得下界取到:
从小到大枚举 i
- i = 0
- 如果 sa[0] > sb[0],那么让 a[0] 向 a[1] 运输 a[0] - b[0]
- 否则,让a[1] 向 a[0] 运输 b[0] - a[0](对于a[1]不够的情况我们允许出现负数,这不会影响i = 1的情况判定,这和洛谷 均分纸牌那题的原理是一样的)
- i = 1......
如此构造下去,我们一定可以构造出一个合法方案
对于本题,因为 sum a 最大不过 10000,所以平方数最多可取 0 x 0,1 x 1,......100 x 100,共101 个数
我们考虑 定义 dp[i, j, k] 为 a[i] = j * j, ∑ j = 0 i a j = k \sum_{j=0}^{i}a_j = k ∑j=0iaj=k ,且 a[0...i] 单调不减的最小代价
我们转移的时候枚举a[i] = j * j,以及 ∑ j = 0 i a j = k \sum_{j=0}^{i}a_j = k ∑j=0iaj=k,那么 dp[i, j, k] = min dp[i - 1, x, k - j * j],我们预处理一下前缀min即可
可以滚动数组优化(见代码)
2、复杂度
时间复杂度: O(100nΣa ) 空间复杂度:O(100Σa)
3、代码详解
c++
#include <bits/stdc++.h>
namespace ranges = std::ranges;
using i64 = long long;
constexpr int inf = 1E9;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
int n;
std::cin >> n;
std::vector<int> a(n);
int M = 0;
for (int i = 0; i < n; ++i) {
std::cin >> a[i];
M += a[i];
}
std::vector dp(101, std::vector<int>(M + 1, inf)), min(101, std::vector<int>(M + 1, inf));
for (int i = 0; i <= 100; ++i) {
min[i][0] = 0;
}
int s = 0;
for (int x : a) {
s += x;
for (int i = 0; i <= 100; ++i) {
for (int j = i * i; j <= M; ++j) {
dp[i][j] = min[i][j - i * i] + std::abs(j - s);
}
}
for (int j = 0; j <= M; ++j) {
min[0][j] = dp[0][j];
for (int i = 1; i <= 100; ++i) {
min[i][j] = std::min(min[i - 1][j], dp[i][j]);
}
}
}
int ans = inf;
for (int i = 0; i < 101; ++i) {
ans = std::min(ans, dp[i][M]);
}
if (ans == inf) {
ans = -1;
}
std::cout << ans << '\n';
}
return 0;
}


