解题思路:方案数问题==动态规划问题。由于只能往下或右走,递归思考,每一点a[i][j]的方案数必由其上方a[i-1][j]或左侧a[i][j-1]得到。问题关键点在于统计的是能被K整除的路径数目,看一下示例1,如果走到(3,3)对3求余为0,因为a[3][3]=2,那么走到a[3][2]或a[2][3]时其余数必须为1。但是我们不能用递归函数去逆推,哪样复杂度会很高。因此需要统计每一个点对K求余的所有方案数,也就是说当我们推导到a[i][j]时,必须知道从起点走到a[i][j]对K求余为0的所有方案数,对K求余为1的所有方案数,对K求余为2的方案数.......求余为K-1的方案数。幸运的是K的值不大于50,因此可以定义二维数组dp[i][j][x],表示当走到(i,j)这个点时对K求余为x的方案数。数学问题,如果小于K的数a和b,使得 (a+b)%k=x,那么a的值是多少呢?
a=(x-b+k)%k。
当然也可以用另一种方法,遍历dp[i-1][j]和dp[i][j-1]的所有余数方案,推出dp[i][j]的所有余数方案。考虑到我和你们都不太会用二维和三维的vector结构....因此下面代码使用滚动数组来实现递推。滚动数组的使用依据是dp[i][j]只会用到其上一行的dp[i-1][j]和同一行的dp[i][j-1]。
class Solution
{
public:
int numberOfPaths(vector<vector<int>>& grid, int k)
{
int dp[2][50005][50]= {0},mod=1e9+7;/**< dp[0]和dp[1]来回滚动 */
int i,j,l,n=grid.size(),m=grid[0].size();/**< n行m列的grid数组 */
dp[0][1][grid[0][0]%k]=1;/**< 第一个元素对K求余 */
for(i=2; i<=m; i++) /**< 计算第一行的所有结果,不然无法滚动...... */
for(j=0; j<k; j++)
if(dp[0][i-1][j])
dp[0][i][(j+grid[0][i-1])%k]=1;
int x=0,y=1;/**< x和y滚起来 */
for(l=2; l<=n; l++)
{
for(i=1; i<=m; i++) /**< 用dp[x]推导出dp[y] */
{
for(j=0; j<k; j++)/**< 先把dp[y]清0 */
dp[y][i][j]=0;
for(j=0; j<k; j++)
{
if(dp[x][i][j])
dp[y][i][(j+grid[l-1][i-1])%k]=(dp[y][i][(j+grid[l-1][i-1])%k]+dp[x][i][j])%mod;
if(dp[y][i-1][j])
dp[y][i][(j+grid[l-1][i-1])%k]=(dp[y][i][(j+grid[l-1][i-1])%k]+dp[y][i-1][j])%mod;
}
}
swap(x,y);/**< 交换x,y实现滚动 */
}
return dp[x][m][0];
}
};
小技巧:一般做这类题目会先把数组所有元素替换为对K求余的结果,虽然我没做这个步骤。
力扣周赛难度较低,可能是为了吸引更多的参赛者吧..............当然要想快速作对也不容易。初学者可以先尝试通过周赛一二题,如果能每天刷几个题,那么周赛三四题经过半年左右的训练就差不多了。