水星人.题解

题目简述

水星人用数字排列代表要表示的数字,如最多使用5个数字情况下,使用 { 1 , 2 , 3 , 4 , 5 } \{1, 2, 3, 4, 5\} {1,2,3,4,5}表示1, { 1 , 2 , 3 , 5 , 4 } \{1, 2, 3, 5, 4\} {1,2,3,5,4}表示2,……, { 5 , 4 , 3 , 2 , 1 } \{5, 4, 3, 2, 1\} {5,4,3,2,1}表示120。
现给定一个水星人的数字序列,以及一个需要加上的值,然后将其转化为水星人的数字表示形式。

输入

N:表示最多使用N位数字,其中 0 < N ≤ 20 0 < N \leq 20 0<N20;
M:表示需要加上的值,其中 0 < M < 2 ∗ 1 0 18 0 < M < 2 * 10^{18} 0<M<21018;
以及N个数字组成的数字序列。

输出

一个数字序列。

思路

数据范围

由于N的范围是不超过20,则总共可以表示的数据个数为不超过 20 ! = 2432902008176640000 20!=2432902008176640000 20!=2432902008176640000,所以,数据范围为long long能够表示,因此可采用序列<==>数字转换问题。

解题思路

因此,该问题转换为两个步骤:

  1. 序列转数字:将一个数字序列转换为相对初始序列的偏移量;
  2. 数字转序列:将一个偏移量还原为一个数字序列。
    如果 N = 4 N=4 N=4,则所有数字范围为 4 ! = 24 4!=24 4!=24,任何一个序列可以转换为相对序列 { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4}的偏移量,每一位的偏移量的基数是相应位数的阶乘
序列转数字
下标1234
阶乘 3 ! = 6 3!=6 3!=6 2 ! = 2 2!=2 2!=2 1 ! = 1 1!=1 1!=1 0 ! = 1 0!=1 0!=1
序列2431
后序较小1210
当前求和 1 ∗ 6 = 6 1*6=6 16=6 2 ∗ 2 = 4 2*2=4 22=4 1 ∗ 1 = 1 1*1=1 11=1 0 ∗ 1 = 0 0*1=0 01=0

所以,序列 { 2 , 4 , 3 , 1 } \{2, 4, 3, 1\} {2,4,3,1}相对初始序列 { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4}的偏移值为 6 + 4 + 1 + 0 = 11 6+4+1+0=11 6+4+1+0=11

数字转序列
下标1234
阶乘 3 ! = 6 3!=6 3!=6 2 ! = 2 2!=2 2!=2 1 ! = 1 1!=1 1!=1 0 ! = 1 0!=1 0!=1

如果偏移值为11,则数字序列的计算过程如下:

  1. 11 ÷ 3 ! = 1 … 5 11\div3!=1…5 11÷3!=15,当前未使用的最小数为1,则第1位应为其后1位未使用数字,为2
  2. 5 ÷ 2 ! = 2 … 1 5\div2!=2…1 5÷2!=21,当前未使用的最小数为1,则第2位应为其后2位未使用数字,则为4
  3. 1 ÷ 1 ! = 1 … 0 1\div1!=1…0 1÷1!=10,当前未使用的最小数为1,则第3位应为其后1位未使用数字,则为3
  4. 0 ÷ 0 ! = 0 … 0 0\div0!=0…0 0÷0!=00,当前未使用的最小数为1,则第4位应该为其后0位未使用数字,则为1

因此,该偏移值转换为序列为 { 2 , 4 , 3 , 1 } \{2, 4, 3, 1\} {2,4,3,1}

AC代码

#include <cstdio>
#define LEN 25
#define LL long long
int bits[LEN];  // 接收的每一位数字
LL factor[LEN];  // 每位数字对应的阶乘
int used[LEN];   // 当前位的数字是否已经使用
int main() {
	int N;
	LL M;	
	scanf("%d%lld", &N, &M);	
	for (int i = 1; i <= N; i++) {  // 接收每位数字
		scanf("%d", &bits[i]);
	}	
	// 计算阶乘(倒排,从高到低)
	factor[0] = 1;
	for (int i = 1; i <= N; i++) {
		factor[i] = factor[i-1] * i;
	}	
	// 计算当前输入序列的偏移值
	LL total = 0;	
	for (int i = 1; i <= N; i++) {
		int times = 0;
		for (int j = i + 1; j <= N; j++) {
			if (bits[j] < bits[i]) {
				times++;
			}
		}		
		total += times * factor[N-i];
	}	
	total += M;	  // 累加
	// 将偏移值转换为数字序列
	for (int i = 1; i <= N; i++) {
		int div = total / factor[N-i];  // 求商
		total %= factor[N-i];	        // 求余数,是下一轮计算的基数
			
		int index = 1;
		while (used[index] != 0) {      // 查找当前未使用的最小数字
			index++;
		}		
		int count = 0;
		while (count < div) {    // 向后移动商的次数
			index++;
			if (used[index] == 0) {  // 只有数字未使用才能计数
				count++;
			}
		}
		
		printf("%d ", index);   // 输出当前数字
		used[index] = 1;        // 并标记为已使用
	}	
	printf("\n");	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值