评价展示序列
拼多多技术岗 8月2号笔试 第二题
题目内容
多多是电商平台的前端开发工程师,负责商品详情页的评价展示模块。评价区收到 NNN 条评价,每条评价有一个星级评分(111 到 555 星)。多多希望将评价按一定顺序展示,使得任意两条评价相邻的星级不同 ,避免连续高分或低分影响用户判断。在所有合法的展示顺序中,请输出 字典序最小 的一种,或报告不可能。
给定 NNN 条评价的星级 r1,r2,...,rNr_1,r_2,\dots,r_Nr1,r2,...,rN (1≤ri≤5)(1 \le r_i \le 5)(1≤ri≤5)。将它们重新排列为一行,使得相邻两个元素的值不同。 如果存在合法排列,输出字典序最小 的一种;如果不存在,输出 −1-1−1。
两个排列 aaa 和 bbb 的字典序比较:从左到右找到第一个 ai≠bia_i \ne b_iai=bi 的位置,若 ai<bia_i < b_iai<bi 则 aaa 的字典序更小。
输入描述
第一行一个整数 NNN (1≤N≤105)(1 \le N \le 10^5)(1≤N≤105)。
第二行 NNN 个整数 r1,r2,...,rNr_1,r_2,\dots,r_Nr1,r2,...,rN (1≤ri≤5)(1 \le r_i \le 5)(1≤ri≤5),表示每条评价的星级。
输出描述
如果不存在合法排列,输出 −1-1−1。
否则输出 NNN 个整数,用空格分隔,表示字典序最小的一种合法排列顺序。
样例1
输入
5
1 1 1 2 3
输出
1 2 1 3 1
说明
样例1:111 出现 333 次,⌈5/2⌉=3\lceil 5/2 \rceil = 3⌈5/2⌉=3,恰好不超限。字典序最小的排列为 (1,2,1,3,1)(1,2,1,3,1)(1,2,1,3,1)。
样例2
输入
4
1 1 1 1
输出
-1
说明
样例2:111 出现 444 次,⌈4/2⌉=2\lceil 4/2 \rceil = 2⌈4/2⌉=2,超过上限,不可能。
样例3
输入
6
5 5 3 5 3 1
输出
1 5 3 5 3 5
说明
样例3:555 出现 333 次,⌈6/2⌉=3\lceil 6/2 \rceil = 3⌈6/2⌉=3,恰好不超限。字典序最小的排列为 (1,5,3,5,3,5)(1,5,3,5,3,5)(1,5,3,5,3,5)(首位取最小可用值 111,此后在保证剩余元素仍可合法排列的前提下逐位取最小)。
题解
思路
解题算法:贪心 + 数学
- 统计每个数字出现次数使用
cnt数组存储。 - 数字能否组成相同数不相邻的序列取决于出现次数最多的
maxCnt。当前可放置位置数量为m时,出现次数最多为maxCnt, 那么要将对应数组合合法序列,需要满足maxCnt + (maxCnt - 1) <= m, 对应需要满足maxCnt <= (m + 1) / 2。maxCnt - 1为最小间隔数量 - 通过2的规律可以判断出初始题目给出的数据是否能构成合法排序。不合法直接返回-1即可。
- 同时要想构造字典序最小的排序方式,可以通过贪心从前往后枚举1, 5确定每个位置元素,判断剩余数字是否能够组成合法序列。
- 判断能否合法序列逻辑在2的基础上要引入额外一个限制,last,如果maxCnt对应元素的上一个确定位置last值时,需要满足
maxCnt + (maxCnt - 1) <= m - 1(第一个位置不能放), 对应需要满足maxCnt <= m / 2 - 算法总体时间复杂度为
O(n)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
bool check(vector<int>& cnt, int last, int remainng) {
if (remainng == 0) {
return true;
}
for (int i = 1; i <= 5; i++) {
int limit = (i == last) ? remainng / 2 : (remainng + 1) /2;
if (cnt[i] > limit) {
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
vector<int> cnt(6, 0);
for (int i = 0; i < N; i++) {
int x;
cin >> x;
cnt[x]++;
}
vector<int> ans;
int last = -1;
bool ok = check(cnt, last, N);
if (!ok) {
cout << "-1\n";
return 0;
}
while (ans.size() < N) {
bool found = false;
// 按从小到大进行尝试
for (int x = 1; x <= 5; x++) {
if (cnt[x] == 0 || x == last) {
continue;
}
cnt[x]--;
if (check(cnt, x, N - 1 - ans.size())) {
ans.push_back(x);
last = x;
found = true;
break;
}
// 恢复
cnt[x]++;
}
// 没有合法选择
if (!found) {
cout << "-1\n";
return 0;
}
}
// 输出合法方案
for (int i = 0; i < N; i++) {
if (i) {
cout << " ";
}
cout << ans[i];
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static boolean check(int[] cnt, int last, int remaining) {
if (remaining == 0) {
return true;
}
for (int i = 1; i <= 5; i++) {
int limit = (i == last) ? remaining / 2 : (remaining + 1) / 2;
if (cnt[i] > limit) {
return false;
}
}
return true;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine().trim());
int[] cnt = new int[6];
String[] nums = br.readLine().trim().split(" ");
for (int i = 0; i < N; i++) {
int x = Integer.parseInt(nums[i]);
cnt[x]++;
}
ArrayList<Integer> ans = new ArrayList<>();
int last = -1;
boolean ok = check(cnt, last, N);
if (!ok) {
System.out.println("-1");
return;
}
while (ans.size() < N) {
boolean found = false;
// 按从小到大进行尝试
for (int x = 1; x <= 5; x++) {
if (cnt[x] == 0 || x == last) {
continue;
}
cnt[x]--;
if (check(cnt, x, N - 1 - ans.size())) {
ans.add(x);
last = x;
found = true;
break;
}
// 恢复
cnt[x]++;
}
// 没有合法选择
if (!found) {
System.out.println("-1");
return;
}
}
// 输出合法方案
StringBuilder sb = new StringBuilder();
for (int i = 0; i < N; i++) {
if (i > 0) {
sb.append(" ");
}
sb.append(ans.get(i));
}
System.out.println(sb);
}
}
python
python
import sys
def check(cnt, last, remaining):
if remaining == 0:
return True
for i in range(1, 6):
limit = remaining // 2 if i == last else (remaining + 1) // 2
if cnt[i] > limit:
return False
return True
N = int(input())
cnt = [0] * 6
nums = list(map(int, input().split()))
for x in nums:
cnt[x] += 1
ans = []
last = -1
ok = check(cnt, last, N)
if not ok:
print("-1")
sys.exit()
while len(ans) < N:
found = False
# 按从小到大进行尝试
for x in range(1, 6):
if cnt[x] == 0 or x == last:
continue
cnt[x] -= 1
if check(cnt, x, N - 1 - len(ans)):
ans.append(x)
last = x
found = True
break
# 恢复
cnt[x] += 1
# 没有合法选择
if not found:
print("-1")
sys.exit()
# 输出合法方案
print(" ".join(map(str, ans)))
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on("line", function(line) {
input.push(line.trim());
});
rl.on("close", function() {
function check(cnt, last, remaining) {
if (remaining === 0) {
return true;
}
for (let i = 1; i <= 5; i++) {
let limit = (i === last)
? Math.floor(remaining / 2)
: Math.floor((remaining + 1) / 2);
if (cnt[i] > limit) {
return false;
}
}
return true;
}
let N = Number(input[0]);
let cnt = new Array(6).fill(0);
let nums = input[1].split(" ").map(Number);
for (let x of nums) {
cnt[x]++;
}
let ans = [];
let last = -1;
let ok = check(cnt, last, N);
if (!ok) {
console.log("-1");
return;
}
while (ans.length < N) {
let found = false;
// 按从小到大进行尝试
for (let x = 1; x <= 5; x++) {
if (cnt[x] === 0 || x === last) {
continue;
}
cnt[x]--;
if (check(cnt, x, N - 1 - ans.length)) {
ans.push(x);
last = x;
found = true;
break;
}
// 恢复
cnt[x]++;
}
// 没有合法选择
if (!found) {
console.log("-1");
return;
}
}
// 输出合法方案
console.log(ans.join(" "));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func check(cnt []int, last int, remaining int) bool {
if remaining == 0 {
return true
}
for i := 1; i <= 5; i++ {
limit := 0
if i == last {
limit = remaining / 2
} else {
limit = (remaining + 1) / 2
}
if cnt[i] > limit {
return false
}
}
return true
}
func main() {
in := bufio.NewReader(os.Stdin)
var N int
fmt.Fscan(in, &N)
cnt := make([]int, 6)
for i := 0; i < N; i++ {
var x int
fmt.Fscan(in, &x)
cnt[x]++
}
ans := make([]int, 0)
last := -1
ok := check(cnt, last, N)
if !ok {
fmt.Println("-1")
return
}
for len(ans) < N {
found := false
// 按从小到大进行尝试
for x := 1; x <= 5; x++ {
if cnt[x] == 0 || x == last {
continue
}
cnt[x]--
if check(cnt, x, N-1-len(ans)) {
ans = append(ans, x)
last = x
found = true
break
}
// 恢复
cnt[x]++
}
// 没有合法选择
if !found {
fmt.Println("-1")
return
}
}
// 输出合法方案
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
for i := 0; i < N; i++ {
if i > 0 {
fmt.Fprint(out, " ")
}
fmt.Fprint(out, ans[i])
}
}