1. 程式人生 > >[遞迴]使用棧解決n輛列車排程問題(列車編號1-n)

[遞迴]使用棧解決n輛列車排程問題(列車編號1-n)

引言:

編號為1-n的n輛列車進棧,要獲得所有正確序列,則可以先通過獲得n輛列車的全排列,然後對每個排列進行正確性檢驗,

即可以獲得所有正確序列。正確性是基於引理,對於一個正確的出棧序列,如果有k<i<j,且a[j]<a[k],則必有a[i]<a[k],簡單來

說就是要滿足小於邊界的所有數必須正序排列。

本程式中,使用迴圈移位方法獲得全排列。迴圈移位的思路是給定一個序列1 2 3 4 .... n。遞迴層次多了很難表達,此處

用1234的思維導圖演示過程。(圖中所有連線均為雙箭頭,第一層遞迴順序是1234->2134->1324->3241->2413->4132->1324)

#include <stdio.h>
#include <malloc.h>
#include <string.h>

void Permutation(int n);
void Recursion(int *a,int n,int k);
void Print(int *a,int n);
void PrintError(int *a,int n);
void GetStackList(int *a,int n,int k);
static int flag = 0;
static int errorNum = 0;
static int totalNum = 0;

int main()
{
    Permutation(12);
    return 0;
}

void Permutation(int n)
{
    int *a = (int *)malloc(sizeof(int)*n);

    for(int i=0;i<n;i++)
    {
        a[i] = i+1;
    }
    Recursion(a,n,2);
    printf("\ninfo:there are %d error list!\n",errorNum);
    printf("info:there are %d total list!\n",totalNum);
    free(a);
}

/**
通過遞迴獲得全排列,全排列獲得演算法採用迴圈移位法
迴圈移位次數 = 迴圈移位的元素個數 = k

**/
void Recursion(int *a,int n,int k)
{
    if(k > n)
    {
        flag = 0;
        totalNum++;
        GetStackList(a,n,n-1);
    }
    else
    {
        int temp;
        for(int i=0;i<k;i++)
        {
            temp = a[0];
            for(int j=1;j<k;j++)
                a[j-1] = a[j];
            a[k-1] = temp;
            Recursion(a,n,k+1);
        }
    }
}

/**
遞迴判斷序列是否正確,每一次判斷的過程是將每個序號和邊界進行大小判定
,若比邊界大,重新修改邊界,再次重複判斷過程。若比邊界小,則該序列是
錯誤的。

**/
void GetStackList(int *a,int n,int k)
{
    int end = a[k];
    unsigned long temp = 4294967295;

    for(int j=k-1;j>=0;j--)
    {
        if(a[j] < end)
        {
            if(a[j] < temp)
            {
                temp = a[j];
                continue;
            }
            if(a[j] > temp && flag != -1)
            {
                PrintError(a,n);
                flag = -1;
                errorNum ++;
                return;
            }
        }
        else
        {
            GetStackList(a,n,j);
        }
    }
}

void PrintError(int *a,int n)
{
    printf("\nthis is error list:");
    for(int i=n-1;i>=0;i--)
    {
        printf("%d ",a[i]);
    }
    printf("\n");
}

void Print(int *a,int n)
{
    for(int i=n-1;i>=0;i--)
    {
        printf("%d ",a[i]);
    }
    printf("\n");
}

n如果超過了10運算量很大,序號10以下所有排列大概有10×9×8×7....×1=300多萬種。

正確的出棧序列可以使用公式計算出來,F(n) = F(0) * F(n-1) + F(1) * F(n-2) + ....... + F(n-1) * F(n-n)

其中F(n-1) = F(0) * F(n-2) + F(1) * F(n-3) + ....... + F(n-1) * F(n-(n-1))

F(1) = F(0) = 1

F(2) = 2

F(3) = 5

F(4) = 14

F(5) = 42

F(6) = 132