HDU 1716 排列2


排列2

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3107    Accepted Submission(s): 1220


Problem Description
Ray又對數字的列產生了興趣:
現有四張卡片,用這四張卡片能排列出很多不同的4位數,要求按從小到大的順序輸出這些4位數。
 

 

Input
每組數據占一行,代表四張卡片上的數字(0<=數字<=9),如果四張卡片都是0,則輸入結束。
 

 

Output
對每組卡片按從小到大的順序輸出所有能由這四張卡片組成的4位數,千位數字相同的在同一行,同一行中每個四位數間用空格分隔。
每組輸出數據間空一行,最后一組數據后面沒有空行。
 

 

Sample Input
1 2 3 4 1 1 2 3 0 1 2 3 0 0 0 0
 

 

Sample Output
1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241 3412 3421 4123 4132 4213 4231 4312 4321 1123 1132 1213 1231 1312 1321 2113 2131 2311 3112 3121 3211 1023 1032 1203 1230 1302 1320 2013 2031 2103 2130 2301 2310 3012 3021 3102 3120 3201 3210
 

 

Source
 

 

Recommend
lcy

 

1,利用next_permutation()函數:

boolean next_permutation(a.begin(),a.end()) 
該函數是以輸入字符串中的字符所構建的按字典順序全排列中,判斷當前字符串之后是否還有下一個字符串
如果next_permutation的執行次數少於全排列的個數,返回true
例如 a="abc" 全排列有 "abc" "acb" "bac" "bca" "cab" "cba"
執行一次next_permutation 返回true a變成 "acb"
再執行一次next_permutation 返回true a變成 "bac"
...
當執行到a="cba" 時 由於這已經是全排列的最后一個字符串,所以 再次執行next_permutation 則返回false
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int main(){

    //freopen("input.txt","r",stdin);

    int a[5],tag=0;
    while(scanf("%d%d%d%d",&a[0],&a[1],&a[2],&a[3])){
        if(a[0]==0 && a[1]==0 && a[2]==0 && a[3]==0)
            break;
        if(tag)
            printf("\n");
        tag=1;
        int flag=1,tmp;
        do{
            if(a[0]==0)
                continue;
            if(flag){
                printf("%d%d%d%d",a[0],a[1],a[2],a[3]);
                flag=0;
            }else if(tmp==a[0])
                printf(" %d%d%d%d",a[0],a[1],a[2],a[3]);
            else
                printf("\n%d%d%d%d",a[0],a[1],a[2],a[3]);
            tmp=a[0];
        }while(next_permutation(a,a+4));
        printf("\n");
    }
    return 0;
}

 

 

2,

引言

對一個給定數據進行全排列,在各種場合經常會用到。組合數學中,生成全排列的方法有很多,盧開澄老師的《組合數學》中就介紹了三種:序數法,字典序法,臨位互換法等。其中以字典序法由於算法簡單,並且使用的時候可以依照當前狀態獲取下一個狀態,直到所有排列全部完成,方便在程序中隨要隨用,應用比較廣泛,STL中的Next_permutation也是使用此法。

 
        

算法定義

首先看什么叫字典序,顧名思義就是按照字典的順序(a-z, 1-9)。以字典序為基礎,我們可以得出任意兩個數字串的大小。比如 "1" < "12"<"13"。 就是按每個數字位逐個比較的結果。對於一個數字串,“123456789”, 可以知道最小的串是 從小到大的有序串“123456789”,而最大的串是從大到小的有序串“*987654321”。這樣對於“123456789”的所有排列,將他們排序,即可以得到按照字典序排序的所有排列的有序集合。

如此,當我們知道當前的排列時,要獲取下一個排列時,就可以范圍有序集合中的下一個數(恰好比他大的)。比如,當前的排列時“123456879”, 那么恰好比他大的下一個排列就是“123456897”。 當當前的排列時最大的時候,說明所有的排列都找完了。

 

於是可以有下面計算下一個排列的算法:

設P是1~n的一個全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn

 

  1)從排列的右端開始,找出第一個比右邊數字小的數字的序號j(j從左端開始計算),即 j=max{i|pi<pi+1}

 

  2)在pj的右邊的數字中,找出所有比pj大的數中最小的數字pk,即 k=max{i|pi>pj}(右邊的數從右至左是遞增的,因此k是所有大於pj的數字中序號最大者)

 

  3)對換pi,pk

 

  4)再將pj+1......pk-1pkpk+1......pn倒轉得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,這就是排列p的下一個排列。

 

 
        

證明

 
要證明這個算法的正確性,我們只要證明生成的下一個排序是恰好比當前排列大的一個序列即可。圖1.11是從盧開澄老師的《組合數學》中截取的一個有1234生成所有排序的字典序樹。從左到右的每一個根到葉子幾點的路徑就是一個排列。下面我們將以這個圖為基礎,來證明上面算法的正確性。
 
算法步驟1,得到的子串 s = {pj+1,.....,pn}, 是按照從大到小進行排列的。即有 pj+1 > pj+2 > ... > pn, 因為 j=max{i|pi<pi+1}。
算法步驟2,得到了最小的比pj大的pk,從n往j數,第一個比j大的數字。將pk和pj替換,保證了替換后的數字比當前的數字要大。
於是得到的序列為p1p2...pj-1pkpj+1...pk-1pjpk-1...pn.注意這里已經將pk替換成了pk。
這時候我們注意到比p1..pj-1pk.....,恰好比p1....pj.....pn大的數字集合。我們在這個集合中挑選出最小的一個即時所要求的下一個排列。
算法步驟3,即是將pk后面的數字逆轉一下(從從大到小,變成了從小到大。)
 
由此經過上面3個步驟得到的下個排列時恰好比當前排列大的排列。
同時我們注意到,當所有排列都找完時,此時數字串從大到小排列。步驟1得到的j = 0,算法結束。
//字典序法生成全排列
#include <iostream>
#include <string>
using namespace std;
string input,s;

void perm(){
    int flag=1;
    if(input[0]!='0'){flag=0;cout<<input;}s=input;
    while(1){
        int index=-1;
        for(int i=input.size()-2;i>=0;i--) // 從最右開始,找到第一個比右邊小的數字,賦值給index
        {
            if(input[i]<input[i+1]){
                index=i;
                break;
            }
        }
        if(index==-1)break; // 所有排列遍歷完,break while

        char M='9'; // M為監視哨兼臨時變量(for swap)
        int C; // C為所找到數字的序號
        for(int i=index+1;i<=input.size()-1;i++){ // 再從最右開始,找到input[index]右邊比input[index]大的數字
            if(input[i]<=input[index])continue;
            if(input[i]<=M){
                C=i;
                M=input[i];
            }
        } 
        input[C]=input[index];
        input[index]=M; // 交換input[index]和input[C]
        int len=input.size()-1-index;
        for(int i=1;i<=len/2;i++){ // 將index后面的部分倒置,比如7421,倒置為1247,只需要對稱交換即可
            char t=input[index+i];
            input[index+i]=input[input.size()-i];
            input[input.size()-i]=t;
        }
        if(input[0]=='0'){ s=input;continue; }
        if(s[0]==input[0]){ cout<<' '<<input;s=input;flag=0; }
        else { if(flag==0)cout<<endl<<input;else cout<<input; s=input; }
    }
    cout<<endl;
}

int main(){
    
    char str[5];
    int cnt=0;
    //freopen("hdu1716in.txt","r",stdin);
    //freopen("hdu1716out.txt","w",stdout);
    while(cin>>str[0]>>str[1]>>str[2]>>str[3])
    { 
        str[4]='\0';input=str;
        if(str[0]=='0'&&str[1]=='0'&&str[2]=='0'&&str[3]=='0')break;
        if(cnt)cout<<endl;cnt++;
        perm();
    }   
    //system("pause");
    //注意:有些系統雖然支持 system("pause"); 
    //但是如果循環會結束,運行到該句就會 Wrong answer
    // 這是應該注意的地方。 
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM