前言:本人是一只正统蒟蒻,不像大佬写的blog十分精炼,清晰,勿喷。
今天有一道很有意思的题:康托展开
这道题感觉有点偏数学,dfs只能得80分,有同学询问GM,得到了这样的答案
厚颜无耻(说的我)
于是我想出了一个稀奇古怪的解法,然后AC了(掌声鼓励),再查了查度娘,发现我的想法是对的,也理解的更加深刻了,现在,我将我的想法写成blog,希望大佬们可以帮我补充
orz
题目描述
给出一个数N,再给出N的全排列的某一个排列,问该排列在全排列中的次序是多少:
例如3的全排列中,123排第一位,321排最后一位。
输入格式
第一行为一个数N,第二行为N的全排列的某一个排列。
输出格式
一个整数,表示该排列在全排列中的次序。
样例输入
样例输入
3
1 2 3
样例输出
1
1.暴力 搜索
思路:暴力枚举每一位上的选法,枚举完后回溯一步。
这个比较简单,就不多说了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 20;
int len;
int a[MAXN], c[MAXN], d[MAXN], jie = 1;//jie来记录现在是第几组答案
bool b[MAXN];
void f(int);
void printf();
int main() {
scanf("%d", &len);
for(int i = 1; i <= len; i++) scanf("%d", &d[i]);
for(int i = 1; i <= len; i++) a[i] = i;
f(1);//枚举第1位
return 0;
}
void f(int s) {//s表示现在在枚举第s位
if(s == len) {//判断到枚举到最后一位了,输出答案
printf();
}
for(int i = 1; i <= len; i++) {//枚举第s位
if(!b[i]) {//判断i这个数用过没有
b[i] = true;//记录下用过i
c[s] = a[i];//顺序记录下来
f(s + 1);//枚举下一位
b[i] = false;//删除记录
}
}
}
void printf() {//输出答案
for(int i = 1; i <= len; i++) {
if(!b[i]) {
c[len] = a[i];
bool flag = true;
for(int j = 1; j <= len; j++) {
if(c[j] != d[j]) {
flag = false;
break;
}
}
if(flag) {
printf("%d", jie);//输出解
}
jie++;//多了一组答案
return ;
}
}
}
注:我这个代码是搜索的正解代码,没有修改,大家可以自行理解,希望大家别ctrl + a 和 ctrl + c 以及 ctrl + v
重头戏来了
2.数学方法
先弄一个5位的:abcde
要让第1位成为a,那么第一位小于a的排列方法要全部加上,所以用乘法原理可得第一位小于a的排列方法:(a - 1)* (5 - 1)!//注意是(5-1)!,因为a并不需要排序
要让第2位成为b,那么在第一位成为a后,第二位小于b的排列方法要全部加上,就是:(b - 1)* (5 - 2)!????????????
做梦
这里就千万别想的太简单了,因为第一位会影响到后面排列。
举个“实在”一点的栗子:2 3 5 4 1
当第一位的2已经确定时:第2位要加上的答案组数的情况只有一种:21…而没有22…(因为不能重复用)
而如果是:3 2 5 4 1的话
当第一位的3已经确定时:第2位要加上的答案组数的情况情况就有两种:31…和32…
那也就是说
要让第2位成为b,那么在第一位成为a后,第二位小于b的排列方法要全部加上,就是:(b - 1 - x)* (5 - 2)!//x是在b之前的小于b的数的个数
同理,就可以算出abcde前面有多少个数,那abcde就是第ans + 1个数
那就简单了啊
因为我是第一次写偏数学的blog,所以有可能表达的不是很清楚 其实就是菜我就举个栗子,帮助大家理解,也希望大佬们可以给我分享一点经验
输入:
5
3 1 2 5 4
第一位:ans += (3 - 1) * (5 - 1)!=> ans += 48
第二位:ans += (1 - 1) * (5 - 2)!=> ans += 0
第三位:ans += (2 - 1 - 1) * (5 - 3)! => ans += 0//前面有一个比2小的1
第四位:ans += (5 - 1 - 3) * (5 - 4)! => ans += 1//前面有三个比5小的数:3 1 2
第五位:ans += (4 - 1 - 3) * (5 - 5)! => ans += 0//前面有3个比4小的数:3 1 2
ans = 49,也就是说,31254前面有49种情况,那31254就是第50个情况
#include <queue>
#include <stack>
#include <cstdio>
#include <climits>
#include <algorithm>
#define LL long long
using namespace std;
const int MAXN = 25;
int n, a[MAXN];
LL sum;
LL jiecheng(int);//算x!
int main() {
scanf("%d", &n);
for(int i = 1; i <= n - 1; i++) {
scanf("%d", &a[i]);
int tot = 0;
for(int j = i; j >= 1; j--)//寻找小于a[i]的数的个数
if(a[j] < a[i]) tot++;
sum += (a[i] - 1 - tot) * jiecheng(n - i);
}
printf("%lld", sum + 1);
return 0;
}
LL jiecheng(int x) {
LL ans = 1;
for(int i = 2; i <= x; i++) ans *= i;
return ans;
}