HDU 1027 Ignatius and the Princess II

        题意:求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;
}


你可能感兴趣的:(HDU)