HDU1027_Ignatius and the Princess II_非模拟做法

Ignatius and the Princess II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 7645    Accepted Submission(s): 4524


Problem Description
Now our hero finds the door to the BEelzebub feng5166. He opens the door and finds feng5166 is about to kill our pretty Princess. But now the BEelzebub has to beat our hero first. feng5166 says, "I have three question for you, if you can work them out, I will release the Princess, or you will be my dinner, too." Ignatius says confidently, "OK, at last, I will save the Princess."

"Now I will show you the first problem." feng5166 says, "Given a sequence of number 1 to N, we define that 1,2,3...N-1,N is the smallest sequence among all the sequence which can be composed with number 1 to N(each number can be and should be use only once in this problem). So it's easy to see the second smallest sequence is 1,2,3...N,N-1. Now I will give you two numbers, N and M. You should tell me the Mth smallest sequence which is composed with number 1 to N. It's easy, isn't is? Hahahahaha......"
Can you help Ignatius to solve this problem?
 

Input
The input contains several test cases. Each test case consists of two numbers, N and M(1<=N<=1000, 1<=M<=10000). You may assume that there is always a sequence satisfied the BEelzebub's demand. The input is terminated by the end of file.
 

Output
For each test case, you only have to output the sequence satisfied the BEelzebub's demand. When output a sequence, you should print a space between two numbers, but do not output any spaces after the last number.
 

Sample Input
  
  
6 4 11 8
 

Sample Output
  
  
1 2 3 5 6 4 1 2 3 4 5 6 7 9 8 11 10

大致题意:

给定一个含有N个元素的数列:1,2,3,4,......N,输出它经过M次变换后的数列。每次变换都是把数列变成比当前数列大的数列,比如 12345 变换一次就是 12354 。12345 与 12435 之间隔着 12354 ,所以 12435 不是变换一次后所得的答案。


大体思路:

网上搜了一下感觉我的思路似乎比较小众,遂决定写一篇题解分享一下。

1)首先讨论一下变换的过程,对于一个数列,我们要把它变成下一个数列,首先考虑把它末两位交换,这样就能得到下一个排列。但是如果末两位已经是降序了,就要移动倒数第三位(如1243,如果把末两位互换,得到的1234比1243还小,是它的上一个排列)。倒数第三位成最后两位中比原倒数第三位大的一个数。如果两个数都比原倒数第三位大,则选其中的较小者。然后继续变换后两位,变换到后三位为严格的降序之后,再变换倒数第四位,以此类推。由于原始数列是严格升序的12345...N,所以变换总是遍历了后面数位的全排列之后,再试图更改靠前的数位。

如:12345->12354->12435->12453->12534->12543->13245......根据这一点创建一个数组T={1,1,2,6,24,120,720,5040},以此确定,要变换第i位,M需要有多大。

2)对于给定的M,将其与T中的元素从后往前依次比较,以找出恰好比M小的那个元素,它的位号就是要变换的数位(倒数第i位)。由于原数列是严格升序的,所以 倒数第i位 和 倒数i-1 位交换后,最后 i-1 位是严格升序的,此时的排列是变换 倒数第i位 后最小的排列。

3)M=M-T[i],重复上述 2) ,知道M==1,表示目标排列是当前排列基础上的第一种排列,即当前排列就是目标排列,任务达成。输出排列即可。

以上是第一版思路,但思路有点缺陷导致了一次WA。

如果M=5,第一次变换交换倒数第二位和倒数第三位,M=M-2。进行第二次变换,仍是交换倒数第二位和倒数第三位,刚刚换好的又给换回来了,相当于啥也没干2333。

这一点小bug却也不难解决。只需要在第一版思路的基础上加加一个变量cnt,记录交换数位的重复次数。还是M=5的例子。第一次交换A[N-i] 和 A[N-i+1],第二次则需要交换A[N-i]和A[N-i+1+1]。其中A[N-i+1+1]里的第二个1就是cnt,我们也可以把第一次交换时的A[N-i+1]写成A[N-i+1+0],那就可以一般化为 交换 A[N-i]和A[N-i+1+cnt]。加上一个cnt,小小的bug便迎刃而解。


总是心里有但是说不出来,这篇题解我自己看了也是一脸蒙圈2333.。。。


[cpp]  view plain  copy
  1. #include<cstdio>  
  2.   
  3. int T[8]={1,1,2,6,24,120,720,5040},A[1005];  
  4. //T用来判定交换的数位,A记录数列的当前状态  
  5.   
  6. int Fun(int x,int N,int l,int cnt)  
  7. //x:目标状态是从当前状态开始数第x种 N:总位数 l:上一次交换的数位 cnt:重复了几次  
  8. {  
  9.     int i;  
  10.     if(x==1) return 0;//达到目标状态,变换结束  
  11.     for(i=7;i>=1&&T[i]>=x;i--);//搜索需交换的数位  
  12.     if(i==l) cnt++;//如果本次交换的数位与上次交换的相同,重复数加一  
  13.     else cnt=0;//如果不同,重复数归零  
  14.     int t=A[N-i];A[N-i]=A[N-i+1+cnt],A[N-i+1+cnt]=t;//交换 A[N-i] 和 A[N-i+1+cnt]  
  15.     Fun(x-T[i],N,i,cnt);//继续变换  
  16.     return 0;  
  17. }  
  18. int main()  
  19. {  
  20.     int N,M;  
  21.     while(scanf("%d%d",&N,&M)!=EOF)  
  22.     {  
  23.         for(int i=1;i<=N;i++) A[i]=i;  
  24.         Fun(M,N,0,0);  
  25.         printf("%d",A[1]);  
  26.         for(int i=2;i<=N;i++)printf(" %d",A[i]);  
  27.         printf("\n");  
  28.     }  
  29.     return 0;  
  30. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值