状态机思路解决动态规划问题

这个状态机是我理解上的状态机,即:在设计算法的时候不只是专注于其中的数字逻辑,因为数字逻辑虽然易于抽象,但是有时会付出极大的时间消耗。

而使用状态机的思路可以增加内存来存储状态进行演绎,但是能够大幅度减少时间消耗

例子:一个击鼓传花的问题:
学校联欢晚会的时候,为了使每一个同学都能参与进来,主持人常常会带着同学们玩击鼓传花的游戏。游戏规则是这样的: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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章