[NOIP 1998 提高组] 拼数
把数字当成字符串,按 x+y 与 y+x 的大小决定拼接顺序,排序后连接得到最大数。
OJ: luogu
题目 ID: P1012
难度:普及-
标签:字符串排序贪心python
日期: 2026-07-06 20:42
题意
给出 n 个正整数,可以任意调整顺序并首尾拼接,要求组成最大的整数。
思路
先看一个小数据暴力:
cpp
// brute.cpp:小数据暴力解,使用选择序列递归枚举所有拼接顺序。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10;
int n;
string a[MAXN];
bool used[MAXN];
int choose_order[MAXN]; // choose_order[i] 表示第 i 个位置放哪个原数
string best_answer;
string calc_answer() {
string res = "";
for (int i = 1; i <= n; i++) {
res += a[choose_order[i]];
}
return res;
}
void update_answer() {
string current = calc_answer();
if ((int)current.size() > (int)best_answer.size() ||
((int)current.size() == (int)best_answer.size() && current > best_answer)) {
best_answer = current;
}
}
void dfs_order(int dep) {
if (dep == n + 1) {
update_answer();
return;
}
// 这一层只记录第 dep 个拼接位置选择哪个数。
for (int i = 1; i <= n; i++) {
if (!used[i]) {
used[i] = true;
choose_order[dep] = i;
dfs_order(dep + 1);
used[i] = false;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
best_answer = "";
dfs_order(1);
cout << best_answer << '\n';
return 0;
}暴力枚举所有排列,再拼接比较,复杂度是 n!。
正解只需要解决两个字符串 x 和 y 的相对顺序:
- 如果
x在前,局部结果是x+y; - 如果
y在前,局部结果是y+x。
为了让整体最大,若 x+y > y+x,就应该让 x 排在 y 前面。按照这个比较规则排序后,把所有字符串连接起来就是答案。
例如 13 和 312:
text
13 + 312 = 13312
312 + 13 = 31213所以 312 应该排在 13 前面。
Python 知识
- 输入数字不要转成整数排序,而要保留为字符串。
- Python 排序默认使用
key,遇到这种“两两比较”规则时,可以用functools.cmp_to_key。 - 比较函数返回负数表示第一个参数排在前面。
代码
python
from functools import cmp_to_key
import sys
def compare(x, y):
if x + y > y + x:
return -1
if x + y < y + x:
return 1
return 0
def main():
tokens = sys.stdin.read().split()
n = int(tokens[0])
numbers = tokens[1:1 + n]
numbers.sort(key=cmp_to_key(compare))
print("".join(numbers))
if __name__ == "__main__":
main()Guide 风格代码
cppbook《C++ 快速入门》教学风格的写法(std:: 前缀、i += 1 循环、0 起始下标):
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-08-14 14:55
* update_at: 2026-08-14 14:55
*/
#include <iostream>
#include <string>
#include <algorithm>
const int max_n = 25; // n <= 20
// 比较规则:x 排在 y 前面能拼出更大的数时返回 true。
// 只看拼接后的 x+y 和 y+x,谁大谁在前
bool better(const std::string& x, const std::string& y) {
return x + y > y + x;
}
int main() {
int n;
std::cin >> n;
std::string a[max_n]; // 数字按字符串保存,避免拼接时溢出
for (int i = 0; i < n; i += 1) {
std::cin >> a[i];
}
std::sort(a, a + n, better); // 按"谁拼在前面更大"排序
for (int i = 0; i < n; i += 1) {
std::cout << a[i]; // 依次拼出最大的整数
}
std::cout << '\n';
return 0;
}复杂度
排序比较次数为 L,每次比较会拼接并比较两个长度约 2L 的字符串,总复杂度约为
空间复杂度是
总结
拼数不能按数值大小排序。真正的局部标准是比较 x+y 和 y+x,谁能让拼接结果更大,谁就排在前面。