贪心加堆:每次合并最小的两个数得 max,取负后每次合并最大的两个数得 min,两遍小根堆后作差,O(N log N)。
OJ: roj
题目 ID: 1427
难度:普及-
标签:贪心堆python
创建: 2026-09-30 09:44
更新: 2026-10-05 23:26
题目描述
黑板上写了 3 / 1 2 3,样例输出 2。(原题面未给出
思路
每次合并当前最小的两个数能得到最大值,每次合并当前最大的两个数能得到最小值——依据是三个数
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 09:45
# update_at: 2026-09-30 09:45
import heapq
import sys
def final_value(nums: list[int], pick_smallest: bool) -> int:
"""按题目规则合并到只剩一个数,返回黑板上剩下的那个值。
pick_smallest 为 True 表示每次取两个最小的数合并(得到最大结果);
否则每次取两个最大的数合并(得到最小结果)。实现上把数取负放进
同一个小根堆,pop 出来的就是原数里最大的两个。
"""
h = list(nums) if pick_smallest else [-x for x in nums]
heapq.heapify(h)
while len(h) > 1:
x, y = heapq.heappop(h), heapq.heappop(h)
# 合并规则 a*b+1;取负方向时重新取负放回,保持堆序含义一致
heapq.heappush(h, x * y + 1 if pick_smallest else -(x * y + 1))
return h[0] if pick_smallest else -h[0]
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
nums = [next(data) for _ in range(n)]
print(final_value(nums, True) - final_value(nums, False))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 23:26
* update_at: 2026-10-05 23:26
*/
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
int n;
ll a[MAXN]; // 黑板上最初的 n 个正整数
void read_input() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
}
// 按题目规则每次合并两个数 a、b 为 a*b+1,直到只剩一个数,返回最终值。
// 求 max 时每次合并当前最小的两个数(小根堆);
// 求 min 时每次合并当前最大的两个数:把数取负放进同一个小根堆,弹出后再还原。
ll merge_all(bool pick_smallest) {
priority_queue<ll, vector<ll>, greater<ll> > heap;
for (int i = 0; i < n; i++) {
if (pick_smallest) {
heap.push(a[i]);
} else {
heap.push(-a[i]);
}
}
while (heap.size() > 1) {
ll x = heap.top();
heap.pop();
ll y = heap.top();
heap.pop();
ll merged = x * y + 1;
if (!pick_smallest) {
merged = -merged; // 放回堆前再取负,保持"堆顶是原数最大者"
}
heap.push(merged);
}
ll res = heap.top();
return pick_smallest ? res : -res;
}
void solve() {
ll max_value = merge_all(true);
ll min_value = merge_all(false);
cout << max_value - min_value << endl;
}
int main() {
read_input();
solve();
return 0;
}