力扣周赛314-矩阵中和能被 K 整除的路径(动态规划)

         解题思路:方案数问题==动态规划问题。由于只能往下或右走,递归思考,每一点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求余的结果,虽然我没做这个步骤。

力扣周赛难度较低,可能是为了吸引更多的参赛者吧..............当然要想快速作对也不容易。初学者可以先尝试通过周赛一二题,如果能每天刷几个题,那么周赛三四题经过半年左右的训练就差不多了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值