题目简述
水星人用数字排列代表要表示的数字,如最多使用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<N≤20;
M:表示需要加上的值,其中
0
<
M
<
2
∗
1
0
18
0 < M < 2 * 10^{18}
0<M<2∗1018;
以及N个数字组成的数字序列。
输出
一个数字序列。
思路
数据范围
由于N的范围是不超过20,则总共可以表示的数据个数为不超过 20 ! = 2432902008176640000 20!=2432902008176640000 20!=2432902008176640000,所以,数据范围为long long能够表示,因此可采用序列<==>数字转换问题。
解题思路
因此,该问题转换为两个步骤:
- 序列转数字:将一个数字序列转换为相对初始序列的偏移量;
- 数字转序列:将一个偏移量还原为一个数字序列。
如果 N = 4 N=4 N=4,则所有数字范围为 4 ! = 24 4!=24 4!=24,任何一个序列可以转换为相对序列 { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4}的偏移量,每一位的偏移量的基数是相应位数的阶乘。
序列转数字
下标 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
阶乘 | 3 ! = 6 3!=6 3!=6 | 2 ! = 2 2!=2 2!=2 | 1 ! = 1 1!=1 1!=1 | 0 ! = 1 0!=1 0!=1 |
序列 | 2 | 4 | 3 | 1 |
后序较小 | 1 | 2 | 1 | 0 |
当前求和 | 1 ∗ 6 = 6 1*6=6 1∗6=6 | 2 ∗ 2 = 4 2*2=4 2∗2=4 | 1 ∗ 1 = 1 1*1=1 1∗1=1 | 0 ∗ 1 = 0 0*1=0 0∗1=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。
数字转序列
下标 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
阶乘 | 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,则数字序列的计算过程如下:
- 11 ÷ 3 ! = 1 … 5 11\div3!=1…5 11÷3!=1…5,当前未使用的最小数为1,则第1位应为其后1位未使用数字,为2;
- 5 ÷ 2 ! = 2 … 1 5\div2!=2…1 5÷2!=2…1,当前未使用的最小数为1,则第2位应为其后2位未使用数字,则为4;
- 1 ÷ 1 ! = 1 … 0 1\div1!=1…0 1÷1!=1…0,当前未使用的最小数为1,则第3位应为其后1位未使用数字,则为3;
- 0 ÷ 0 ! = 0 … 0 0\div0!=0…0 0÷0!=0…0,当前未使用的最小数为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;
}