这道题目,最直观的想法是求出1到n的所有排列,然后将全部排列排序.

但是,n最大可以是1024,1024!个排 列,几乎永远也算不出来,算出来也没有地方存放。那么,有没有公式或规律,能够很快由一个排列推算出下k个排列呢?

实际上寻找规律或公式都是徒劳的,只能老老实实由给定排列算出下一个排列,再算出下一个排列……一直算到第k的排列。

鉴于k的值很小,最多只有64,因此这种算法应该是可行的。

如何由给定排列求下一个排列?

不妨自己动手做一下。比如:“2 1 4 7 6 3 5”的下一个排列是什么?

容易,显然是“2 1 4 7 6 5 3”,那么,再下一个排列是什么?有点难了,是“2 1 5 3 4 6 7”。

以从“2 1 4 7 6 5 3”求出下一个排列“2 1 5 3 4 6 7”作为例子,可以总结出求给定排列的下一个排列的步骤:

假设给定排列中的n个数从左到右是a1, a2, a3……an。

1)从an开始,往左边找,直到找到某个aj,满足aj-1<aj

(对上例j, 这个aj就是7, aj-1 就是4)。

2)在aj、aj+1…… an中找到最小的比aj-1大的数,将这个数和aj-1互换位置

(对上例, 这个数就是5,和4换完位置后的排列是“2 1 5 7 6 4 3”)。

3)将从位置j到位置n的所有数(共n-j+1个)从小到大重新排序,排好序后,新的排列就是所要求的排列。

(对上例,就是将“7 6 4 3”排序,排好后的新排列就是“2 1 5 3 4 6 7”)。

当然,按照题目要求,如果a1, a2, a3……an已经是降序,那么它的下一个排序就是an, an-1, an-2……a1。

注:上面的是基本思路,但是我们可以根据当前排列的性质,加以优化;

对于第一步,没法优化,只好从后到前找,找到了J。

第二布就可以优化了。循环找是可以,但是从J到J+1是倒序的,这样的话,根据有序的这个特性可以用二分查找,效率由O(n)提到到了O(log(n)).

第三部,排序仍是不需要的,因为从J到J+1是倒序的,只需反转就OK 了,降到了O(n).

接下来粘上代码以供参考

 1 #include<stdio.h>
 2 const int N=1015;
 3 int str[N];
 4 int n,k;
 5 
 6 void restr(int begin)
 7 {
 8     int end=n-1;
 9     int p;
10     while(end>begin)
11     {
12         p=str[end];
13         str[end]=str[begin];
14         str[begin]=p;
15         end--;
16         begin++;
17     }
18 
19 }
20 
21 int find(int p,int begin,int end)
22 {
23     if(begin==end)return begin;
24     if(begin+1==end)
25     {
26         if(str[end]>str[p])return end;
27         return begin;
28     }
29     
30     int mid=(begin+end)/2;
31     if(str[mid]<=str[p])return find(p,begin,mid-1);
32     return find(p,mid,end);
33 }
34 
35 
36 void serch(int p)
37 {
38     int p0=find(p-1,p,n-1);//find the first number that is bigger than the value of str[p-1]
39     int p1=str[p-1];
40     str[p-1]=str[p0];
41     str[p0]=p1;
42 }
43 
44 void next()
45 {
46     int i=n-1;    
47     while(i && str[i-1]>=str[i])i--;
48     if(i==0){restr(0);return ;}
49     serch(i);
50     restr(i);            
51 }
52 
53 void print()
54 {
55     printf("%d",str[0]);
56     for(int i=1;i<n;i++)
57     {
58         printf(" %d",str[i]);
59     }
60     printf("\n");
61 }
62 
63 int main()
64 {
65 
66     scanf("%d",&n);
67     while(scanf("%d%d",&n,&k)!=EOF)
68     {
69         for(int i=0;i<n;i++)scanf("%d",&str[i]);    
70         if(n!=1)
71         {
72             while(k--)
73             {
74                 next();    
75             }        
76         }
77 
78         
79         print();
80     }
81 return 0;    
82 }

 

 

 

 

 

posted on 2012-04-11 16:27  tiankonguse  阅读(297)  评论(0编辑  收藏  举报