HDU 1027 Ignatius and the Princess II

本文介绍如何使用康托展开的逆过程解决求第M小全排列的问题,并提供了一个有效的算法实现,通过分析排列的变化规律,实现了快速定位特定排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        题意:求N个数(1,2,3,...,N-1,N)的全排列中第M小的数。

        思路:康托展开逆过程。

        因为前些天刚学输出全排列,看到这道题就有一个想法,从小到大求全排列并计数,求到第M个的时候输出,结果果断超时了。又仔细思考了一下,发现这个题和康托展开很像,康托展开是求一个序列在全排列中的次序,这个题刚好反过来,求第M小的排列。于是开始找规律,在纸上列了一些sample,发现从右往左第k+1位增加1,能导致序列的次序上升k!,然后算法自然就出来了。这道题还有个很有意思的地方,M<=10000,这就意味着就算M达到上限,也只能影响到最后的8位数(7!=5040),而N的上限可是1000啊,前面那么多位只要按顺序输出就好了。


#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <memory.h>
#include <vector>
#include <queue>
#include <stack>
#include <ctype.h>

using namespace std;


int table[8]={1,1,2,6,24,120,720,5040};
int add[8];
int last8[7];


int main(){
	int n,m;
	
	while(~scanf("%d%d",&n,&m)){
		memset(add,0,sizeof(add));
		m--;//最小的排列,如1 2 3 4 5算第0个,所以m需要减去1 
		for(int i=7;i>=0;i--){
			while(m>=table[i]){
				m-=table[i];
				add[i]++;
			}
		}

		for(int i=1;n-i>7;i++){//除了后8位,按顺序输出 
			printf("%d ",i);
		}
		
		
		for(int i=7;i>=0;i--){
			last8[i]=n-i;
		}
		
		for(int i=7;i>=0;i--){
			if(i>n-1)continue;
			if(add[i]){
				int tmp=last8[i-add[i]];
				for(int j=i-add[i];j<i;j++){//某一位增加以后,处理顺序 
					last8[j]=last8[j+1];
				}
				last8[i]=tmp;
			}
			printf("%d",last8[i]);
			if(i)printf(" ");
		}
		printf("\n");
		
		
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值