康拓展开蒟蒻解释

前言:本人是一只正统蒟蒻,不像大佬写的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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值