做这道题花了很多时间,所以没有长期的训练,想要在短时间内解决这种题目还是有一定的难度。
du熊的机器人
Description
du熊正在玩一个别人刚送给它的机器人。这个机器人只能在一个棋盘中行走,棋盘的左上角格子为(0, 0),右下角格子为(X, Y)。
du熊控制这个机器人从棋盘的左上角,走到右下角,再从右下角回到左上角。当机器人从左上角走到右下角的过程中,如果它当前所在格子为(x, y),则它只能走到(x+1, y)或(x, y+1)的格子;当机器人从右下角走回左上角的过程中,如果它当前所在的格子为(x, y),则它只能走到(x-1, y)或(x, y-1)的格子。并且du熊要求机器人从左上角走到右下角再走回左上角的整个过程中,最多经过同一个格子一次。
请你帮du熊计算出这个机器人一共有多少种不同的路线。
Input
输入的第一行为一个正整数T(0<T<=50),表示数据组数。
接下来的T行,每行有两个整数,分别表示X和Y。(1<=X,Y<=1000)
Output
对每组输入数据,输出一个整数,表示机器人不同的路线数量。
Sample Input
3
1 1
1 1000
3 4
Sample Output
2
2
100
问题分析:
群举是第一个直觉的做法,但题目里要求X,Y可以到1000,而X,Y达到10就已经是比较大的一个数,这个数目至少相当于从20的数中选10个出来。
第二想法是动态规划,因为每个点的运动使其构成树结构,但状态转移也不能很容易的找出来
定义S(m,n,x):满足某种条件的路径的数目,这些条件包括:
1、从坐标(0,0)出发,经过(m,n),终点是(0,1)
2、经过每个坐标最多一次
3、经过(x,0)
任一条逆时针可以走的路径,顺时针也满足题目要求 ,所以
2*S(m,n,0)就是题目要求的值。而
根据我们的定义,终点是(0,1),那么第一步一定是(0,0)->(1,0),S(m,n,0)等于S(m,n,1)。
接下来定义的状态转移:
终点是
(0,1),可以从两个方向到达(0,1)
1、最后一步(1,1)->(0,1)
这种情况下,满足条件的路径有这样的形式:(0,0)->(1,0)->...(m,n)->...->(1,1)->(0,1)
其数目等于
形式为
(1,0)->...(m,n)->...->(1,1)的所有满足条件的路径的数目,等于形式为(0,0)->...(m-1,n)->...->(0,1)的的所有满足条件的路径的数目,等于S(m-1,n,x-1)
2、最后一步(0,2)->(0,1)
由定义,一定存在某一步:(x,0)->(x,1),这个是显然的
对每个1<=x<=m,我们计算满足形式(0,0)->...->(x,0)->(x,1)->...->
(m,n)->...->(0,2)->(0,1)的所有路径的数目,定义这个数目为t(m,n,x)
所有经过
(x,0)形式满足(0,0)->...->(x,0)...->(m,n)->...->(0,2)->(0,1)的路径的数目为sum(t(m,n,x:m))
怎么求t(m,n,x)?观察可以发现,t(m,n,x)恰好等于S(m,n-1,x)
总结一下
S(m,n,x)= S(m-1,n,x-1) + sum(S(m,n-1,x:m)
边界条件:
我们总要要求起始点是(0,0)终点是(0,1),则S(m,0,x)=0,应为没有
终点(0,1)不存在
S(0,n,x) = 0,
S(0,0,0)=1,
S(0,1,0)=1,
这几个条件画个图都能简单的推出来
空间复杂度:O(M*M*N),时间复杂度
O(M*M*N)
最后一个问题,考虑到结算的结果特别大,状态转移矩阵可能要使用大数的数据结构
我的实现
#define AL 1000+1
long pathCount(const size_t M, const size_t N){
long S[AL][AL][AL];
for(size_t n = 0; n <= N; n++)
S[0][n][0] = 0;
for(size_t m = 0; m <= M; m++)
for(size_t x = 0; x <= M; x++)
S[m][0][x] = 0;
S[0][0][0] = 1;
S[0][1][0] = 1;
for(size_t m = 1; m <= M; m++){
for(size_t n = 1; n <= N; n++){
long tmpS = 0;
for(size_t x = m;x >= 1 ; x--){
tmpS += S[m][n - 1][x];
S[m][n][x] = S[m-1][n][x-1] + tmpS;
}
S[m][n][0] = S[m][n][1];
}
}
return 2*S[M][N][0];
}