pku acm 1833 排列

这道题目,最直观的想法是求出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 }

 

 

 

 

 

你可能感兴趣的:(ACM)