经典算法 排列的字典序问题
问题描述
给定 n
个元素 {1, 2, ..., n}
,它们一共有 n!
个不同的排列。将这 n!
个排列按字典序进行排列,并从 0 开始编号为 0, 1, ..., n! - 1
。
每个排列对应的编号称为它的字典序值。
例如,当 n = 3
时,所有排列的字典序值如下:
字典序值 | 排列 |
---|---|
0 | 1 2 3 |
1 | 1 3 2 |
2 | 2 1 3 |
3 | 2 3 1 |
4 | 3 1 2 |
5 | 3 2 1 |
算法设计
给定 n
以及 {1, 2, ..., n}
的一个排列,请你计算:
- 该排列的字典序值
- 该排列的下一个排列(按字典序排列)
如果该排列已经是最大的(即字典序值为 n! - 1
),则输出 -1
表示没有下一个排列。
数据输入
- 第 1 行是一个整数
n
,表示元素个数; - 第 2 行是一个长度为
n
的排列,包含{1, 2, ..., n}
中的所有元素,元素之间用空格分隔。
数据输出
- 第 1 行输出该排列的字典序值;
- 第 2 行输出该排列的下一个排列,若不存在则输出
-1
。
输入样例
8
2 6 4 5 8 1 7 3
输出样例
8227
2 6 4 5 8 3 1 7
c++代码(暴力法,O(n^2))
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
int n, sum = 0, k = 1, cont = 0;
vector<int> arr, know;
int main() {
scanf("%d", &n);
arr = vector<int>(n), know = vector<int>(n + 1, 1);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
for (int i = 1; i <= n - 1; i++) k *= i;
for (int i = 0; i < n - 1; i++) {
cont = 0;
for (int j = 1; j < arr[i]; j++) {
if (know[j]) cont++;
}
sum += cont * k;
k /= (n - i - 1);
know[arr[i]] = 0;
}
next_permutation(arr.begin(), arr.end());
printf("%d\n", sum);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}//by wqs
c++代码(线段树优化,O(nlogn))
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
class node {
public:
int val; //权值
int lazy; //懒标记
};
int n, sum = 0, k = 1, cont = 0;
vector<int> arr, know;
vector<node> nodes;
void build(int index, int left, int right) {
if (left == right) {
nodes[index].val = know[left];
nodes[index].lazy = 0;
return;
}
int middle = (left + right) / 2;
build(2 * index, left, middle);
build(2 * index + 1, middle + 1, right);
nodes[index].val = nodes[2 * index].val + nodes[2 * index + 1].val;
nodes[index].lazy = 0;
}
void pssslazy(int cur, int cur_l, int cur_r) {
int middle = (cur_l + cur_r) / 2;
if (nodes[cur].lazy != 0) {
nodes[2 * cur].val += (middle - cur_l + 1) * nodes[cur].lazy;
nodes[2 * cur + 1].val += (cur_r - middle) * nodes[cur].lazy;
nodes[2 * cur].lazy += nodes[cur].lazy;
nodes[2 * cur + 1].lazy += nodes[cur].lazy;
nodes[cur].lazy = 0;
}
}
void add(int cur, int cur_l, int cur_r, int left, int right, int k) {
int middle = (cur_l + cur_r) / 2;
if (cur_l >= left && cur_r <= right) {
nodes[cur].val += (cur_r - cur_l + 1) * k;
nodes[cur].lazy += k;
return;
}
pssslazy(cur, cur_l, cur_r);
if (left <= middle) add(2 * cur, cur_l, middle, left, right, k);
if (right > middle) add(2 * cur + 1, middle + 1, cur_r, left, right, k);
nodes[cur].val = nodes[2 * cur].val + nodes[2 * cur + 1].val;
}
int ask(int cur, int cur_l, int cur_r, int left, int right) {
int middle = (cur_l + cur_r) / 2;
if (cur_l >= left && cur_r <= right) return nodes[cur].val;
pssslazy(cur, cur_l, cur_r);
int ans = 0;
if (left <= middle) ans += ask(2 * cur, cur_l, middle, left, right);
if (right > middle) ans += ask(2 * cur + 1, middle + 1, cur_r, left, right);
return ans;
}
int main() {
scanf("%d", &n);
arr = vector<int>(n), know = vector<int>(n + 1, n);
nodes = vector<node>(4 * n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
know[i + 1] = i;
}
build(1, 1, n);
for (int i = 1; i <= n - 1; i++) k *= i;
for (int i = 0; i < n - 1; i++) {
cont = ask(1, 1, n, arr[i], arr[i]);
sum += cont * k;
k /= (n - i - 1);
add(1, 1, n, arr[i] + 1, n, -1);
}
next_permutation(arr.begin(), arr.end());
printf("%d\n", sum);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}//by wqs