狀態機思路解決動態規劃問題

這個狀態機是我理解上的狀態機,即:在設計算法的時候不只是專注於其中的數字邏輯,因爲數字邏輯雖然易於抽象,但是有時會付出極大的時間消耗。

而使用狀態機的思路可以增加內存來存儲狀態進行演繹,但是能夠大幅度減少時間消耗

例子:一個擊鼓傳花的問題:
學校聯歡晚會的時候,爲了使每一個同學都能參與進來,主持人常常會帶着同學們玩擊鼓傳花的遊戲。遊戲規則是這樣的:n個同學坐着圍成一個圓圈,指定一個同學手裏拿着一束花,主持人在旁邊背對着大家開始擊鼓,鼓聲開始之後拿着花的同學開始傳花,每個同學都可以把花傳給自己左右的兩個同學中的一個(左右任意),當主持人停止擊鼓時,傳花停止,此時,正拿着花沒傳出去的那個同學就要給大家表演一個節目。
聰明的小賽提出一個有趣的問題:有多少種不同的方法可以使得從小賽手裏開始傳的花,傳了m次以後,又回到小賽手裏。對於傳遞的方法當且僅當這兩種方法中,接到花的同學按接球順序組成的序列是不同的,才視作兩種傳花的方法不同。比如有3個同學1號、2號、3號,並假設小賽爲1號,花傳了3次回到小賽手裏的方式有1->2->3->1和1->3->2->1,共2種。
輸入共一行,有兩個用空格隔開的整數n,m(3<=n<=30,1<=m<=30)
輸出共一行,有一個整數,表示符合題意的方法數

首先:不適用狀態機的解決方案(代碼在後邊)
:遍歷每種可能的狀態,判斷是不是傳回給了自己
當傳遞次數增加的時候,狀態的可能性呈指數增加:而事實上,並不需要針對每個中間過程中的狀態來判斷,只需要最後停止時候的狀態的可能性

思路:
第一步:建立狀態陣
對於N個鏈式節點,狀態陣爲長度爲N的內存
長度爲N的一段狀態,這裏的狀態只有兩種情況,可以選擇Bool活着int,int可以統計數據,更加理想
爲了存儲m次傳遞之後的狀態,這裏擴充內存結構爲:
代碼:shared_ptr<int> MatirxCount = new int[n][m];
用智能指針管理動態數組;
第二步:逐次模擬結果狀態
1. 初始化狀態:MatrixCount[1][0] = 1;
2. 每次傳遞:將MatrixCount[i][j]向周圍擴散,每次傳遞後的數據向下一行記錄;
關鍵步驟:每次1擴散到節點都代表有一種將花傳遞到該節點的方案
3. 循環擴散m次
第三步:拿到結果並輸出
要求的是輸出傳回給自己的方案數,那麼只訪問統計m次以後,MatrixCount[1][m]的值即可

具體實現:
int main()
{
    int n, m;
    cin >> n >> m;

    // 擴容:n+1增加一個單元形成閉環;m+1:初始狀態佔一個位置
    vector<vector<int>> MatrixCount(m + 1, vector<int>(n + 2, 0));

    // 開始逐次模擬
    MatrixCount[0][1] = 1;
    MatrixCount[0][n+1] = 1;
    for (int i = 1; i <= m; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
// 每次都將矩陣中的狀態向下擴散
            MatrixCount[i][j] = MatrixCount[i-1][j-1] + MatrixCount[i-1][j+1];
        }
        MatrixCount[i][0] = MatrixCount[i][n];	// 用計算彌補數據結構的不足
        MatrixCount[i][n+1] = MatrixCount[i][1];
    }

    cout << MatrixCount[m][1];		// 拿到狀態統計結果
    return 0;
}




附加一個遍歷所有中間狀態的算法:在次數過多的時候無法執行
bool isComeBack(long Numbers, int circle, int times)
{
    int tempSummary = 0;
    for (int i = 0; i < times; ++i)
    {
        tempSummary += (Numbers % 2 == 1 ? 1 : -1);
        Numbers /= 2;
    }
    if (tempSummary % circle == 0)
        return true;
    else
        return false;
}

int main()
{
    int m, n;
    cin >> n >> m;
    int count = 0;
    for (long i = 0; i < pow((double)2, m); ++i)
    {
        if (isComeBack(i, n, m))
            count++;
    }
    cout << count;
    return 0;
}


發佈了34 篇原創文章 · 獲贊 88 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章