算法记录:击鼓传花问题c语言实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dengminghli/article/details/77619543

题目描述

学校联欢晚会的时候,为了使每一个同学都能参与进来,主持人常常会带着同学们玩击鼓传花的游戏。游戏规则是这样的: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)

 输出

输出共一行,有一个整数,表示符合题意的方法数

输入用例:
3 3
输出用例:
2

思路分析:

由于n人围成的是一个循环体,并且每个人都只可以往相邻的两个人的方向传递花。也就是说,每个人都往下一个人传时,都可以产生两种情况。同时传递次数会减去其1。由此,假设我们设定起点位置的编号为0,那么我们可以假设一个二维数组dp来记录每个编号人员能够在多少次传递次数中传递到起点位置,其横列表示次数,纵列表示当前人员的编号。而我们针对两种情况可能出现序列个数的总和,则为我们的正解。也就是我们得出这么一条方程:
dp[m][n] = dp[m-1][n-1] +dp[m-1][n+1]  
在这个方程上,其实就可以构成一种类递归结构。也是动态规划结构的初始模型,在这个问题上,我们进行进一步的完善:
1.结束条件:
在这个问题上,并不是每种情况都会在次数用完之前回到原点,因此,我们需要对能够回到原点的行为进行标识。也就是,当可用次数为1时,距离起点0附近的两个节点(1 || n-1 )能够立刻返回至起点中,此时也就是说
dp[1][1] = 1;
dp[1][n-1] = 1;
2.需要进行多少遍循环:
由于我们需要探究的是每一次传递都会有哪些操作,也就是说,我们需要确保每一次次数的消耗都需要和他的情况,也就是左右两边的人在m-1次次数下到达起始点的个数。由此我们既需要对每个编号人员及其次数进行遍历。因为次数引导编号,所以次数是活跃循环。因此,我们定义两个循环,外层循环遍历人员编号,内层则遍历次数。在这里需要注意的一个问题是,循环问题,也就是当n-1之后的左边人员会是0.因此需要进行处理。
综述:可得出以下状态转移方程,以i代表当前次数,j代表当前人员编号:
dp[i][j] = dp[i-1][(n-j-1)%n]+dp[i-1][(n-i+1)%n];

得出状态转移方程和结束调节,我们便可以完善代码了:

#include<stdio.h>
int dptest(int n,int m);
int main(void){
    int m,n;
    scanf("%d %d",&n,&m);
    int result = dptest(n,m);
    printf("%d\n",result);
    return 0;
}

int dptest(int n,int m){
        int dp[31][31];
        dp[1][1] = 1;
        dp[1][n - 1] = 1;
    for (int i = 2; i <= m; i++) {
        for (int j = 0; j < n; j++) {
            dp[i][j] = dp[i - 1][(n-j-1) % n] + dp[i - 1][(n-j+1) % n];
        }
    }
    return dp[m][0];
}       


猜你喜欢

转载自blog.csdn.net/dengminghli/article/details/77619543