#P4267. 第1题-令人心动
-
ID: 3345
Tried: 27
Accepted: 4
Difficulty: 2
所属公司 :
百度
时间 :2025年10月19日
-
算法标签>搜索枚举
第1题-令人心动
解题思路
-
核心目标:通过若干次“把某个数
ai
拆成两个正整数相加”的操作,使数组满足max(a) ≤ 2 × min(a)
。 -
关键观察:
- 拆分会让元素变小,但不能让当前最小值变小,否则条件更难满足。因此最优策略是不拆分最小值,并在拆分其他元素时尽量让拆出的每一段都 ≥ 最小值
m
。 - 若数组最小值为
m
,只要把每个元素ai
拆成若干段,使每段都 ≤ 2m 且 ≥ m,就能保证最终整体满足条件,同时不降低m
。
- 拆分会让元素变小,但不能让当前最小值变小,否则条件更难满足。因此最优策略是不拆分最小值,并在拆分其他元素时尽量让拆出的每一段都 ≥ 最小值
-
化为计数问题:
- 要把
ai
切成p
段,使每段的最大值ceil(ai / p) ≤ 2m
,则最小可行段数为p_min = max(1, ceil(ai / (2m)))
。 - 需要的操作次数是把一个数分成
p
段需p-1
次,因此对该ai
的最少操作是max(0, ceil(ai / (2m)) - 1)
。
- 要把
-
算法:
- 扫一遍求最小值
m
。 - 对每个
ai
累加max(0, ceil(ai / (2m)) - 1)
。 - 总和即答案。
- 扫一遍求最小值
-
实现要点:
- 使用整除上取整:
ceil(x / y) = (x + y - 1) // y
。 - 全程用 64 位整型避免溢出(Java
long
、C++long long
)。
- 使用整除上取整:
复杂度分析
- 时间复杂度:
O(n)
,一次扫描求最小值,再一次扫描累加。 - 空间复杂度:
O(1)
(不计输入存储)。
代码实现
Python
# 题面功能在外部函数里,主函数只做输入输出
import sys
def min_operations_to_pleasant(arr):
# 计算使数组满足 max <= 2*min 的最少拆分次数
m = min(arr)
twice = 2 * m
ans = 0
for a in arr:
# 对每个元素累加 max(0, ceil(a/(2m)) - 1)
need = (a + twice - 1) // twice # ceil(a / (2m))
if need > 1:
ans += need - 1
return ans
def main():
data = sys.stdin.read().strip().split()
n = int(data[0])
arr = list(map(int, data[1:1+n]))
print(min_operations_to_pleasant(arr))
if __name__ == "__main__":
main()
Java
// ACM 风格,类名统一为 Main
import java.io.*;
import java.util.*;
public class Main {
// 外部函数:计算最少拆分次数
public static long minOperationsToPleasant(long[] arr) {
long m = Long.MAX_VALUE;
for (long v : arr) m = Math.min(m, v); // 最小值
long twice = 2L * m;
long ans = 0;
for (long a : arr) {
// 需要的段数 need = ceil(a / (2m))
long need = (a + twice - 1) / twice;
if (need > 1) ans += need - 1; // 累加操作次数
}
return ans;
}
public static void main(String[] args) throws Exception {
// 数据范围较大但行数少,默认输入方式即可
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s1 = br.readLine();
while (s1 != null && s1.trim().isEmpty()) s1 = br.readLine();
int n = Integer.parseInt(s1.trim());
String[] parts = br.readLine().trim().split("\\s+"); // 用空白分割
long[] arr = new long[n];
for (int i = 0; i < n; i++) arr[i] = Long.parseLong(parts[i]);
System.out.println(minOperationsToPleasant(arr));
}
}
C++
// ACM 风格:主函数读写,功能写在外部函数
#include <bits/stdc++.h>
using namespace std;
// 计算最少拆分次数
long long minOperationsToPleasant(const vector<long long>& a) {
long long m = *min_element(a.begin(), a.end()); // 最小值
long long twice = 2LL * m;
long long ans = 0;
for (long long x : a) {
// need = ceil(x / (2m)) = (x + 2m - 1) // (2m)
long long need = (x + twice - 1) / twice;
if (need > 1) ans += need - 1; // 每个元素增加的操作次数
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<long long> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
cout << minOperationsToPleasant(a) << "\n";
return 0;
}
题目内容
在游戏中,主人公 Tk有一个长度为{a1,a2,...,an}的数组 ; 他将一个数组称为令人心动,当且仅当该数组的最大值不超过最小值的两倍,即max(a1,...,an)≤2×min(a1,...,an); 为了让自己的数组变为令人心动,他可以进行以下操作多次,直到满足条件:
- 选择一个元素ai,并选取两个正整数 x,y满足x+y=ai ,将ai删除,并将x,y插入数组中; 请你计算使数组变为令人心动所需的最少操作次数。
输入描述
第一行输入一个整数n(1≤n≤2×105),表示数组的长度; 第二行输入n个整数a1,a2,...,an(1≤ai≤109),表示数组中的元素。
输出描述
输出一个整数,表示将数组变为令人心动的最少操作次数。
样例1
输入
3
3 6 13
输出
2
说明
- 说明 在此样例中,初始数组为 {3,6,13},操作方案不唯一;
- 将 13 拆分为5 和8,数组变为{3,6,5,8} ;
- 将 8 拆分为 4和4,数组变为{3,6,5,4,4} ,此时最大值 6 不超过最小值 3 的两倍,满足条件,共需 2次操作。