有 N 堆石子,每堆的石子数量分别为 a1,a2,…,aN。
你可以对石子堆进行合并操作,将两个相邻的石子堆合并为一个石子堆,例如,如果 a=[1,2,3,4,5],合并第 2,3 堆石子,则石子堆集合变为 a=[1,5,4,5]。
我们希望通过尽可能少的操作,使得石子堆集合中的每堆石子的数量都相同。
请你输出所需的最少操作次数。
本题一定有解,因为可以将所有石子堆合并为一堆。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N。
第二行包含 N 个整数 a1,a2,…,aN。
输出格式
每组数据输出一行结果。
数据范围
1≤T≤10,
1≤N≤10^5,
0≤ai≤10^6,
∑i=1nai≤10^6,
每个输入所有 N 之和不超过 10^5。
输入样例:
3
6
1 2 3 1 1 1
3
2 2 3
5
0 0 0 0 0
输出样例:
3
2
0
题意 :
- 任意合并相邻的两个石子堆,问使得所有石子堆中石子数量相同的最少合并次数
思路 :
- 假设最少操作次数为x,则最终石子堆的数量是n - x,且每一堆的石子数量y为 ∑ a i n − x \frac{\sum{a_i}}{n-x} n−x∑ai
- 因此,n-x为 ∑ a i \sum{a_i} ∑ai的约数,要x最小,即n-x最大,因此我们从大到小枚举n-x,如果n-x为 ∑ a i \sum{a_i} ∑ai的约数,且在这种情况下每堆的石子数量y满足条件(模拟题意),即找到最少操作次数
- 在判断是否满足条件时,我们考虑每一个石子堆,它要么不参与合并,要么与相邻的石子堆合并(这说明最终合并结果必然是初始所有石子堆被划分成了一段一段一段…),因此我们只需顺序遍历初始石子堆,累加每一个石子堆的值至s,如果s等于y,则置s为0为下一段计数;否则如果s大于y,首先在这个石子堆的上一个石子堆处s是小于y的,而更前面的所有石子堆都满足了条件,不可能拆给后面的段,因此一旦s大于y,说明方案失败
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, a[N];
bool check(int y) {
for (int i = 0, s = 0; i < n; ++ i) {
s += a[i];
if (s == y) s = 0;
else if (s > y) return false;
}
return true;
}
int main() {
int _; cin >> _;
while (_ -- ) {
cin >> n;
int sum = 0;
for (int i = 0; i < n; ++ i) {
cin >> a[i];
sum += a[i];
}
for (int i = n; i; -- i) {
if (sum % i == 0 && check(sum / i)) {
cout << n - i << endl;
break;
}
}
}
}