当前位置: 首页 > news >正文

经典算法 排列的字典序问题

问题描述

给定 n 个元素 {1, 2, ..., n},它们一共有 n! 个不同的排列。将这 n! 个排列按字典序进行排列,并从 0 开始编号为 0, 1, ..., n! - 1

每个排列对应的编号称为它的字典序值

例如,当 n = 3 时,所有排列的字典序值如下:

字典序值排列
01 2 3
11 3 2
22 1 3
32 3 1
43 1 2
53 2 1

算法设计

给定 n 以及 {1, 2, ..., n} 的一个排列,请你计算:

  1. 该排列的字典序值
  2. 该排列的下一个排列(按字典序排列)

如果该排列已经是最大的(即字典序值为 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

相关文章:

  • 为什么需要 Node.js 的 URL 处理工具?
  • JavaScript 函数参数详解
  • 后端实现加解密工具类(记录)
  • MySQL增删改查(CRUD)操作详解与实战指南
  • Java EE(17)——网络原理——IP数据报结构IP协议解析(简述)
  • TabularDataset
  • 蓝桥杯备考----》完全背包模板
  • HarmonyOs学习 实验四:开发一个登录界面
  • Electron 系统托盘与屏幕捕捉深度解析:从基础到企业级实践
  • 【Linux】网络概念
  • STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)
  • [Vue2]侦听器watch(监视器)
  • Android JobScheduler调度任务面试题及参考答案
  • 01_现代C++特殊成员函数
  • Android 10上如何查看GPU占用率 安卓手机怎么看gpu频率
  • 介绍一下JVM内存结构面试回答(后续会继续补充)
  • Netty源码—10.Netty工具之时间轮
  • QT 动态布局实现(待完善)
  • 【docker】Dockerfile中ENTRYPOINT和CMD区别理解
  • c语言strcat和strlen的注意事项
  • 如何做网站数据报表/摘抄一篇新闻
  • 重庆如何软件网站推广/微信公众号怎么开通
  • 电子商务网站建设需求说明书/百度推广的渠道有哪些
  • 山东省建设监理协会网站6/全国疫情排行榜最新情况列表
  • wordpress调用相关文章加速/成都百度seo公司
  • fusion做电影网站卡死/全国疫情高中低风险区一览表