题目叙述
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
示例 1:
输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3
输出:3
解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。
示例 2:
输入:n = 3, relation = [[0,2],[2,1]], k = 2
输出:0
解释:信息不能从小 A 处经过 2 轮传递到编号 2
限制:
2 <= n <= 10
1 <= k <= 5
1 <= relation.length <= 90, 且 relation[i].length == 2
0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]
解题思路
一开始没想到动态规划和递归思想,一开始执着于遍历数组一个一个找与之相对应的另一个点进行跳跃,太过麻烦。下列借鉴了下题解区大佬们的思想并写出了自己的理解。
注意int p[k][n]为花费k步到n-1编号
动态规划:
第一步.首先确定状态:
最后一步问题时:在到达n-1之前,前一步一定在0…n-2之间
分成子问题就是:走k-1步能够到达0…n-2中任意一个的方法数,他们的和就是总数量
定义状态p[k][n]为走k步到达n-1的方法数量
第二步.定义状态转移方程
p[k][n]=sum(p[k-1][0]+dp[k-1][1]+…+p[k-1][n-2]+p[k-1][n-1])
第三步.初始值和边界条件
初始值:p[0][0]=1,走0步到达0节点的方法为1,此也是关键
边界条件:k,n限制
代码
int numWays(int n, int** relation, int relationSize, int* relationColSize, int k){
int bu = k+1;//此为定义步数的限制
int bh = n;//此为定义编号的限制,即小孩子的个数。
int p[bu][bh];
memset(p,0,sizeof(p));//先对此数组初始化为0
p[0][0]=1;//此为花费0步数到达节点(即到达左端点)0的方法数目,由题可知,初始坐标已经确定,所以此步骤即
int i,j; //在于确定一个原点
for(i=1;i<bu;++i)//这一步是为了对花费从0步到k步到各个节点的方法数目计算起到限制,即i<bu
{
for(j=0;j<relationSize;++j)//这一步由下面j放的位置可知是为了起到确定relation数组的每一个所给的
{ //“区间”都会被用到一次
p[i][relation[j][1]]+=p[i-1][relation[j][0]];
}//这一步为动态规划核心,即花费2步到某一节点a1的方法数目和花费1步到a1对应的左端点a2方法数目,而花
}//费一步到a2又会被分成花费0步到a2对应左端点a3。
return p[k][n-1];//经过上述循环体的不断给各个步数到各个节点的方法数目运算,最后到要得到的值运算返回
}//时,由于各个方法数已经确定所以直接加和即可。且正是由于开始的初始化数组全为0和p[0][0]=1,而导致
//不符合的,不从原点开始的他们的方法数一直为0,加不上去。