#P3400. 第3题-打乱数组排列
-
1000ms
Tried: 53
Accepted: 14
Difficulty: 6
所属公司 :
字节
时间 :2025年8月17日
-
算法标签>贪心算法
第3题-打乱数组排列
思路
- 核心贪心:先取当前未用元素中的最大值作为开头,记当前前缀的 gcd 为 g。随后每一步在剩余元素 x 中选取使 gcd(g,x) 最大的那个;若并列,取 x 更大的。选中后令 g←gcd(g,x) 并继续,直至使用完所有元素。
- 直觉:让前缀的 g 尽量大、下降尽量慢,可以提升以当前末尾为右端点的所有子数组的 gcd 之和,从而提升总 S。该策略会自然把 1 往后推,把具有公共因子的数聚在一起。
- 复杂度:每步在至多12种取值中挑选,整体 O(n⋅12),满足 n 规模。
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<int> cnt(13, 0);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
cnt[a[i]]++;
}
// 结果序列
vector<int> ans;
ans.reserve(n);
// 选取当前未用元素中的最大值作为开头
int cur = 0;
for (int v = 12; v >= 1; --v) {
if (cnt[v]) {
cur = v;
cnt[v]--;
ans.push_back(v);
break;
}
}
// 逐步选择使 gcd(cur, x) 最大的元素(并列取值更大者)
for (int used = 1; used < n; ++used) {
int bestG = -1, pick = -1;
for (int v = 12; v >= 1; --v) {
if (!cnt[v]) continue;
int g = std::gcd(cur, v);
if (g > bestG || (g == bestG && v > pick)) {
bestG = g;
pick = v;
}
}
// 放入选择的元素,更新当前前缀 gcd
cnt[pick]--;
cur = std::gcd(cur, pick);
ans.push_back(pick);
}
// 输出
for (int i = 0; i < n; ++i) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
return 0;
}
Python
import sys
import math
def solve():
data = sys.stdin.read().strip().split()
if not data:
return
it = iter(data)
n = int(next(it))
cnt = [0] * 13
arr = [int(next(it)) for _ in range(n)]
for x in arr:
cnt[x] += 1
ans = []
# 选取当前未用元素中的最大值作为开头
cur = 0
for v in range(12, 0, -1):
if cnt[v]:
cur = v
cnt[v] -= 1
ans.append(v)
break
# 每次选择使 gcd(cur, x) 最大的元素(并列取值更大)
for _ in range(1, n):
bestG, pick = -1, -1
for v in range(12, 0, -1):
if cnt[v] == 0:
continue
g = math.gcd(cur, v)
if g > bestG or (g == bestG and v > pick):
bestG, pick = g, v
cnt[pick] -= 1
cur = math.gcd(cur, pick)
ans.append(pick)
print(' '.join(map(str, ans)))
if __name__ == "__main__":
solve()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner(System.in);
int n = fs.nextInt();
int[] cnt = new int[13];
for (int i = 0; i < n; i++) {
int x = fs.nextInt();
cnt[x]++;
}
StringBuilder sb = new StringBuilder();
List<Integer> ans = new ArrayList<>(n);
// 选取当前未用元素中的最大值作为开头
int cur = 0;
for (int v = 12; v >= 1; v--) {
if (cnt[v] > 0) {
cur = v;
cnt[v]--;
ans.add(v);
break;
}
}
// 每步选择使 gcd(cur, x) 最大的元素(并列取值更大)
for (int used = 1; used < n; used++) {
int bestG = -1, pick = -1;
for (int v = 12; v >= 1; v--) {
if (cnt[v] == 0) continue;
int g = gcd(cur, v);
if (g > bestG || (g == bestG && v > pick)) {
bestG = g;
pick = v;
}
}
cnt[pick]--;
cur = gcd(cur, pick);
ans.add(pick);
}
for (int i = 0; i < ans.size(); i++) {
if (i > 0) sb.append(' ');
sb.append(ans.get(i));
}
System.out.println(sb.toString());
}
static int gcd(int a, int b) {
while (b != 0) {
int t = a % b;
a = b;
b = t;
}
return a;
}
// 轻量读入
static class FastScanner {
BufferedInputStream in;
byte[] buffer = new byte[1 << 16];
int ptr = 0, len = 0;
FastScanner(InputStream is) { in = new BufferedInputStream(is); }
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buffer);
ptr = 0;
if (len <= 0) return -1;
}
return buffer[ptr++];
}
int nextInt() throws IOException {
int c, sgn = 1, x = 0;
do { c = read(); } while (c <= 32);
if (c == '-') { sgn = -1; c = read(); }
while (c > 32) {
x = x * 10 + (c - '0');
c = read();
}
return x * sgn;
}
}
}
题目内容
Tk 有一个长度为 n 的数组 {a1,a2,...,an} 。定义函数 g(i,j) 表示数组从下标 i 到 j 的元素的最大公约数,即 g(i,j)=gcd(ai,ai+1,...,aj) 。特别低,若 i=j,则 g(i,j)=ai 。
Tk 希望将数组重新打乱排列,使得所有 非空子数组的 g(i,j) 之和尽可能大:
S=∑i=1n∑j=1ng(i,j)
你需要输出一种重新排列后的数组,该排列能使 S 达到最大值。
最大公约数,指多个整数共有约数中最大的一个。例如,12 和 30 的公约数有 1,2,3,6 ,其中最大的约数是 6 ,因此 gcd(12,30)=6 。
非空子数组 为从原数组中,连续的选择一段元素(可以全选、不可以不选)得到的新数组。
输入描述
第一行输入一个正整数 n(1≦n≦2×105) ,代表数组的长度。
第二行输入 n 个正整数 a1,a2,...,an(1≦ai≦12) ,代表数组的元素。
输出描述
输出一个长度为 n 的数组,代表重新排列后的数组。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
样例1
输入
3
2 1 4
输出
2 4 1
说明
在这个样例中,其中一种最优的重新排列后的数组为 {2,4,1} ,此时有:
-
g(1,1)=a1=2;
-
g(1,2)=gcd(a1,a2)=2;
-
g(1,3)=gcd(a1,a2,a3)=1;
-
g(2,2)=a2=4;
-
g(2,3)=gcd(a2,a3)=1;
-
g(3,3)=a3=1。
总和 S=2+2+1+4+1+1=11 。可以证明这是最大能达到的值。