【摘录】《程序设计导引及在线实践》之排列
生活随笔
收集整理的這篇文章主要介紹了
【摘录】《程序设计导引及在线实践》之排列
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
問題描述 大家知道,給出正整數(shù)n,則1 到n 這n 個數(shù)可以構(gòu)成n!種排列,把這些排列按照從 小到大的順序(字典順序)列出,如n=3 時,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1 六個排列。 給出某個排列,求出這個排列的下k 個排列,如果遇到最后一個排列,則下1 排列為第 1 個排列,即排列1 2 3…n。 比如:n = 3,k=2 給出排列2 3 1,則它的下1 個排列為3 1 2,下2 個排列為3 2 1,因 此答案為3 2 1。 輸入數(shù)據(jù) 第一行是一個正整數(shù) m,表示測試數(shù)據(jù)的個數(shù),下面是m 組測試數(shù)據(jù),每組測試數(shù)據(jù) 第一行是2 個正整數(shù)n( 1 <= n < 1024 )和k(1<=k<=64),第二行有n 個正整數(shù),是1,2 … n 的一個排列。 輸出要求 對于每組輸入數(shù)據(jù),輸出一行,n 個數(shù),中間用空格隔開,表示輸入排列的下k 個排列。 輸入樣例 3 3 1 2 3 1 3 1 3 2 1 10 2 1 2 3 4 5 6 7 8 9 10 輸出樣例 3 1 2 1 2 3 1 2 3 4 5 6 7 9 8 10 解題思路 這道題目,最直觀的想法是求出 1 到n 的所有排列,然后將全部排列排序……且慢,n 最大可以是1024,1024! 個排列,幾乎永遠也算不出來,算出來也沒有地方存放。那么, 有沒有公式或規(guī)律,能夠很快由一個排列推算出下k 個排列呢?實際上尋找規(guī)律或公式都是 徒勞的,只能老老實實由給定排列算出下一個排列,再算出下一個排列……一直算到第k 的排列。鑒于k 的值很小,最多只有64,因此這種算法應(yīng)該是可行的。 如何由給定排列求下一個排列?不妨自己動手做一下。比如: “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”作為例子,可以總結(jié)出求給定排 列的下一個排列的步驟: 假設(shè)給定排列中的n 個數(shù)從左到右是a1, a2, a3……an 。 1) 從 an 開始,往左邊找,直到找到某個aj,滿足aj-1 < aj(對上例, 這個aj 就是 7, aj-1 就是4)。 2) 在 aj 、aj+1…… an 中找到最小的比aj-1 大的數(shù),將這個數(shù)和 aj-1 互換位置(對 上例, 這個數(shù)就是5,和4 換完位置后的排列是 “2 1 5 7 6 4 3”)。 3) 將從位置 j 到位置n 的所有數(shù)(共n-j+1 個)從小到大重新排序,排好序后, 新的排列就是所要求的排列。(對上例,就是將“7 6 4 3”排序,排好后的新排列就是“2 1 5 3 4 6 7”)。 當(dāng)然,按照題目要求,如果a1, a2, a3……an 已經(jīng)是降序,那么它的下一個排序就是an, an-1, an-2……a1 。 參考程序: 1. #include <stdio.h> 2. #include <stdlib.h> 3. #define MAX_NUM 1024 4. int an[MAX_NUM + 10]; 5. //用以排序的比較函數(shù) 6. int MyCompare( const void * e1, const void * e2) 7. { 8. return * ((int *) e1) - * ((int *) e2); 9. } 10. 11. main() 12. { 13. int M; 14. int n, k, i, j; 15. scanf("%d", & M); 16. for (int m = 0; m < M; m ++ ) { 17. scanf("%d%d", &n, &k); 18. //排列存放在 an[1] .... an[n] 19. for( i = 1; i <= n; i ++ ) 20. scanf("%d", &an[i]); 21. an[0] = 100000; //確保an[0]比排列中所有的數(shù)都大 22. for( i = 0; i < k ;i ++ ) { //每次循環(huán)都找出下一個排列 23. for( j = n ; j >= 1 && an[j-1] > an[j] ; j -- ) ; 24. if( j >= 1 ) { 25. int nMinLarger = an[j]; 26. int nMinIdx = j; 27. //下面找出從an[j]及其后最小的比 an[j-1]大的元素,并記住其下標(biāo) 28. for( int kk = j; kk <= n; kk ++) 29. if( nMinLarger > an[kk] && an[kk] > an[j-1]) { 30. nMinLarger = an[kk]; 31. nMinIdx = kk; 32. } 33. //交換位置 34. an[nMinIdx] = an[j-1]; 35. an[j-1] = nMinLarger; 36. qsort( an + j, n - j + 1 , sizeof(int), MyCompare); //排序 37. } 38. else { //an 里的排列已經(jīng)是降序了,那么下一個排列就是 1 2 3 。。。。 n 39. for( j = 1; j <= n; j ++ ) 140 40. an[j] = j; 41. } 42. } 43. for( j = 1; j <= n; j ++ ) 44. printf("%d ", an[j]); 45. printf("\n"); 46. 47. } 48. } 語句36 是對一個數(shù)組的局部進行排序。qsort 函數(shù)并不要求第一個參數(shù)必須是一個數(shù)組 的開始地址,只要是待排序的一片連續(xù)空間的開始地址即可。同樣,qsort 的第二個參數(shù)也 不必一定是整個數(shù)組的元素個數(shù),只要是待排序的元素個數(shù)即可。 實現(xiàn)技巧 1.把排列存放在an[1] .... an[n],而在an[0]存放一個比排列中所有的數(shù)都大的數(shù),這個 an[0]所起的作用通常稱之為“哨兵”。有了“哨兵”,就可以寫語句23: for( j = n ; j >= 1 && an[j-1] > an[j] ; j -- ) ; 而23 不必擔(dān)心j-1 小于0 導(dǎo)致數(shù)組越界。如果沒有“哨兵”,而且將排列存放在an[0] .... an[n-1]中,那么寫到相當(dāng)于語句23 的這個for 循環(huán)的時候,就要判斷j-1 小于0 的情況,比 較羅嗦,也容易出錯。 放置“哨兵”,是在數(shù)組或鏈表中進行各種操作時常用的做法。 2. 學(xué)過C++標(biāo)準(zhǔn)模板庫的同學(xué)會注意到,用標(biāo)準(zhǔn)模板庫中的next_permutation 算法直 接就能求給定排列的下一個排列,根本不需動腦筋。 常見問題 這個題目的測試數(shù)據(jù)比較多,用 scanf 讀入沒有問題,有的同學(xué)學(xué)了點C++, 用C++中 的cin 讀入數(shù)據(jù),就會造成超時。
轉(zhuǎn)載于:https://www.cnblogs.com/skywter/archive/2010/09/17/1829609.html
總結(jié)
以上是生活随笔為你收集整理的【摘录】《程序设计导引及在线实践》之排列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL字段去掉空格
- 下一篇: silverlight 不可