货运装载顺序校验(JAVA/Py/C/C++/Js/Go)题解
华为OD机试真题 新系统 华为OD上机考试真题新系统 10月21号 200分题型
华为OD机试新系统真题目录点击查看: 华为OD机试新系统真题题库目录|机考题库 + 算法考点详解
题目内容
某物流分拣中心每天接收大量货运订单,每条订单带有一个「优先级」编号(编号越小,优先级越高,越应优先装载)。订单按到达系统的时间先后排成队列,因上游调度延迟,到达顺序与优先级顺序经常不一致。
分拣中心定义了一种「装载逆序」:对于队列中到达在前的订单 i i i 与到达在后的订单 j j j(即 i < j i < j i<j),若订单 i i i 的优先级编号严格大于 订单 j j j,则这两个订单构成一个装载逆序。
现给定装载优先级数组 priorities,其中 priorities[i] 表示第 i i i 个到达的订单的优先级编号,请返回装载逆序的总数。数组元素可能重复,重复值之间不构成逆序。
输入描述
输入只有一行,形如:
p0,p1,...,p(n-1)
元素之间用半角逗号分隔。
取值范围:
- 1 ≤ n ≤ 50000 1 \le n \le 50000 1≤n≤50000,其中 n n n 为
priorities的长度; - 0 ≤ priorities i ≤ 10 5 0 \le \text{priorities}i \le 10^5 0≤prioritiesi≤105;
- 数组元素可能存在重复值;只有严格大于才构成装载逆序。
输出描述
输出一行一个十进制整数,表示装载逆序的总数。
数据保证答案不超过 50000 × 49999 / 2 = 1249975000 50000 \times 49999 / 2 = 1249975000 50000×49999/2=1249975000;标程仍统一使用 64 位整数累加,留足余量。
样例1
输入
5,3,2,4,1
输出
8
说明
- 构成的装载逆序对为: ( 5 , 3 ) (5,3) (5,3)、 ( 5 , 2 ) (5,2) (5,2)、 ( 5 , 4 ) (5,4) (5,4)、 ( 5 , 1 ) (5,1) (5,1)、 ( 3 , 2 ) (3,2) (3,2)、 ( 3 , 1 ) (3,1) (3,1)、 ( 2 , 1 ) (2,1) (2,1)、 ( 4 , 1 ) (4,1) (4,1),共 8 8 8 对。
- 队列中优先级编号最大( 5 5 5)的订单排在最前,与后面所有订单都构成逆序;编号 1 1 1 排在最后,与前面所有比它大的订单都构成逆序。
样例2
输入
1,2,3,4,5
输出
0
说明
- 队列已按优先级编号升序排列(编号从小到大),每个订单都排在应处的位置。
- 不存在「先到达但优先级更低」的错位,装载逆序数为 0 0 0。
样例3
输入
3,1,2
输出
2
说明
- 逐对检查: 3 > 1 3 > 1 3>1 构成逆序, 3 > 2 3 > 2 3>2 构成逆序, 1 < 2 1 < 2 1<2 不构成逆序。
- 编号 3 3 3 排在首位却优先级最低,与后两个订单均构成逆序; 1 1 1 和 2 2 2 的相对顺序正确,不构成逆序;共 2 2 2 对。
解题思路
思路:树状数组
-
题目给出的优先级范围为
0-100000,因此可以使用树状数组进行求解。树状数组有两个核心方法,简单说说- query(x): 查询
[1,x]的前缀和值 - update(x): 更新
[x,maxN]的前缀和值
- query(x): 查询
-
从左往右遍历数组,使用树状数组维护已经出现过的优先级数量,对于当前元素
p[i]- 查询已经出现、优先级小于等于当前值的元素数量
num。 - 用已经遍历的元素减去num,就得到之前比当前元素大的数量,就是当前元素产生的逆序对数量。
- 将当前元素加入树状数组。
- 查询已经出现、优先级小于等于当前值的元素数量
-
按照上述逻辑即可求出对应逆序对数量。
C++
cpp
#include <bits/stdc++.h>
#include <vector>
using namespace std;
using ll = long long;
// 根据p范围决定
const int MAXV = 100001;
// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<int> split(const string& str, const string& delimiter) {
vector<int> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != string::npos) {
result.push_back(stoi(str.substr(start, end - start)));
start = end + delimiter.length();
end = str.find(delimiter, start);
}
// 添加最后一个部分
result.push_back(stoi(str.substr(start)));
return result;
}
int lowbit(int x) {
return x & -x;
}
// 树状数组查询
int query (int x, vector<int>& tree) {
int sum = 0;
while (x > 0) {
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
// 更新
void update(int x, vector<int>& tree) {
while (x <= MAXV) {
tree[x]++;
x += lowbit(x);
}
}
ll solve(vector<int>& p) {
int n = p.size();
vector<int> tree(MAXV + 1, 0);
long long ans = 0;
for (int i = 0; i < p.size(); i++) {
// 将优先级映射到树状数组下标,避免下标为0
int x = p[i] + 1;
// 已遍历元素中,严格大于当前值的数量
ans += i - query(x, tree);
update(x, tree);
}
return ans;
}
int main() {
string input;
getline(cin, input);
vector<int> p = split(input, ",");
cout << solve(p);
return 0;
}
JAVA
java
import java.util.*;
public class Main {
// 根据p范围决定
static final int MAXV = 100001;
// lowbit
static int lowbit(int x) {
return x & -x;
}
// 树状数组查询
static int query(int x, int[] tree) {
int sum = 0;
while (x > 0) {
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
// 更新
static void update(int x, int[] tree) {
while (x <= MAXV) {
tree[x]++;
x += lowbit(x);
}
}
static long solve(int[] p) {
int n = p.length;
int[] tree = new int[MAXV + 1];
long ans = 0;
for (int i = 0; i < n; i++) {
// 将优先级映射到树状数组下标,避免下标为0
int x = p[i] + 1;
// 已遍历元素中,严格大于当前值的数量
ans += i - query(x, tree);
update(x, tree);
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
String[] tokens = input.split(",");
int[] p = new int[tokens.length];
for (int i = 0; i < tokens.length; i++) {
p[i] = Integer.parseInt(tokens[i]);
}
System.out.println(solve(p));
}
}
Python
python
# 根据p范围决定
MAXV = 100001
def lowbit(x):
return x & -x
# 树状数组查询
def query(x, tree):
sum_val = 0
while x > 0:
sum_val += tree[x]
x -= lowbit(x)
return sum_val
# 更新
def update(x, tree):
while x <= MAXV:
tree[x] += 1
x += lowbit(x)
def solve(p):
n = len(p)
tree = [0] * (MAXV + 1)
ans = 0
for i in range(n):
# 将优先级映射到树状数组下标,避免下标为0
x = p[i] + 1
# 已遍历元素中,严格大于当前值的数量
ans += i - query(x, tree)
update(x, tree)
return ans
if __name__ == "__main__":
input_str = input().strip()
p = list(map(int, input_str.split(",")))
print(solve(p))
JavaScript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 根据p范围决定
const MAXV = 100001;
// lowbit
function lowbit(x) {
return x & -x;
}
// 树状数组查询
function query(x, tree) {
let sum = 0;
while (x > 0) {
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
// 更新
function update(x, tree) {
while (x <= MAXV) {
tree[x]++;
x += lowbit(x);
}
}
function solve(p) {
const n = p.length;
const tree = new Int32Array(MAXV + 1);
let ans = 0;
for (let i = 0; i < n; i++) {
// 将优先级映射到树状数组下标,避免下标为0
const x = p[i] + 1;
// 已遍历元素中,严格大于当前值的数量
ans += i - query(x, tree);
update(x, tree);
}
return ans;
}
rl.on("line", (input) => {
const p = input.trim().split(",").map(Number);
console.log(solve(p));
rl.close();
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// 根据p范围决定
const MAXV = 100001
// lowbit
func lowbit(x int) int {
return x & -x
}
// 树状数组查询
func query(x int, tree []int) int {
sum := 0
for x > 0 {
sum += tree[x]
x -= lowbit(x)
}
return sum
}
// 更新
func update(x int, tree []int) {
for x <= MAXV {
tree[x]++
x += lowbit(x)
}
}
func solve(p []int) int64 {
n := len(p)
tree := make([]int, MAXV+1)
var ans int64 = 0
for i := 0; i < n; i++ {
// 将优先级映射到树状数组下标,避免下标为0
x := p[i] + 1
// 已遍历元素中,严格大于当前值的数量
ans += int64(i - query(x, tree))
update(x, tree)
}
return ans
}
func main() {
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
tokens := strings.Split(input, ",")
p := make([]int, len(tokens))
for i, token := range tokens {
p[i], _ = strconv.Atoi(token)
}
fmt.Println(solve(p))
}
C语言
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 根据p范围决定
#define MAXV 100001
// lowbit
int lowbit(int x) {
return x & -x;
}
// 树状数组查询
int query(int x, int tree[]) {
int sum = 0;
while (x > 0) {
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
// 更新
void update(int x, int tree[]) {
while (x <= MAXV) {
tree[x]++;
x += lowbit(x);
}
}
long long solve(int p[], int n) {
int tree[MAXV + 1] = {0};
long long ans = 0;
for (int i = 0; i < n; i++) {
// 将优先级映射到树状数组下标,避免下标为0
int x = p[i] + 1;
// 已遍历元素中,严格大于当前值的数量
ans += i - query(x, tree);
update(x, tree);
}
return ans;
}
int main() {
char input[2000005];
fgets(input, sizeof(input), stdin);
int p[200005];
int n = 0;
// 切割字符串
char* token = strtok(input, ",\r\n");
while (token != NULL) {
p[n++] = atoi(token);
token = strtok(NULL, ",\r\n");
}
printf("%lld\n", solve(p, n));
return 0;
}