zoukankan      html  css  js  c++  java
  • leetcode_935. Knight Dialer_动态规划_矩阵快速幂

     https://leetcode.com/problems/knight-dialer/

     在如下图的拨号键盘上,初始在键盘中任意位置,按照国际象棋中骑士(中国象棋中马)的走法走N-1步,能拨出多少种不同的号码。

    解法一:动态规划,逆向搜索

    class Solution
    {
    public:
        vector<vector<int> > gra{{4,6},{6,8},{7,9},{4,8},{0,3,9},
            {},{0,1,7},{2,6},{1,3},{2,4}};
        const int mod=1e9+7;
        int knightDialer(int N)
        {
            int res=0;
            for(int i=0; i<=9; i++)
            {
                vector<vector<int>> dp(N+1,vector<int>(10,-1));
                dp[0][i]=1;
                for(int j=0;j<=9;j++)
                    res = (res+dfs(N-1,j,dp))%mod;
            }
            return res;
        }
        int dfs(int step,int num,vector<vector<int>>& dp)
        {
            if(dp[step][num]>=0)
                return dp[step][num];
            if(step==0)
                return dp[step][num]=0;
            int ret=0;
            for(int i=0;i<gra[num].size();i++)
                ret = (ret + dfs(step-1, gra[num][i], dp))%mod;
            return dp[step][num]=ret;
        }
    };

    解法二:动态规划,正向递推

    class Solution
    {
    public:
        vector<vector<int> > gra{{4,6},{6,8},{7,9},{4,8},{0,3,9},
            {},{0,1,7},{2,6},{1,3},{2,4}};
        const int mod=1e9+7;
        int knightDialer(int N)
        {
            int res=0;
            for(int i=0; i<=9; i++)
            {
                vector<vector<int>> dp(N+1,vector<int>(10,0));
                dp[0][i]=1;
                for(int j=1; j<=N-1; j++)
                    for(int k=0; k<=9; k++)
                        for(int l=0; l<gra[k].size(); l++)
                            dp[j][k] = (dp[j][k]+dp[j-1][gra[k][l]])%mod;
                for(int j=0; j<=9; j++)
                    res = (res+dp[N-1][j])%mod;
            }
            return res;
        }
    };

    问题一:要构造10次二维的vector,很耗时,dp[N][10]空间也有很大浪费。

    改进:

    dp[j][k] = (dp[j][k]+dp[j-1][gra[k][l]])%mod;(当前状态由前一时刻状态推得)
    改为dp[j+1][gra[k][l]] = (dp[j+1][gra[k][l]]+dp[j][k])%mod;(由当前时刻状态推下一时刻状态)
    改进过后可以省去9次构造二维vector的开销,除此之外,递推更加高效(相比之下少了一层for)。
    class Solution
    {
    public:
        vector<vector<int> > gra{{4,6},{6,8},{7,9},{4,8},{0,3,9},
            {},{0,1,7},{2,6},{1,3},{2,4}};
        const int mod=1e9+7;
        int knightDialer(int N)
        {
            int res=0;
            int dp[5000][10];
            //vector<vector<int>> dp(N,vector<int>(10,0));
            memset(dp,0,sizeof(dp));
            for(int i=0; i<=9; i++)
                dp[0][i]=1;
            for(int j=0; j<=N-2; j++)
                for(int k=0; k<=9; k++)
                    for(int l=0; l<gra[k].size(); l++)
                        dp[j+1][gra[k][l]] = (dp[j+1][gra[k][l]]+dp[j][k])%mod;
            for(int j=0; j<=9; j++)
                res = (res+dp[N-1][j])%mod;
            return res;
        }
    };

     空间复杂度还没有还没优化,但是可以发现,递推关系只需要两个状态(当前状态和下一步状态),而不需要N个状态。

    解法三:动态规划,矩阵快速幂

    进一步使用矩阵运算来优化状态的递推关系,同时还可以使用快速幂,使最终时间复杂度优化到O(logN),空间复杂度优化到常数量级。但是C++自己实现矩阵稍微有点麻烦。使用python的numpy非常方便。

    class Matrix
    {
    public:
        Matrix(int row, int col);
        Matrix(vector<vector<int>>& v);
        Matrix operator * (const Matrix& rh)const;
        Matrix& operator = (const Matrix& rh);
        int GetRow(){return row_;}
        int GetCol(){return col_;}int SumOfAllElements();
        ~Matrix();
    private:
        int row_,col_;
        long long **matrix_;
    };
    Matrix::Matrix(int row, int col)
    {
        row_ = row;
        col_ = col;
        matrix_ = new long long* [row_];
        for(int i=0; i<row_; i++)
            matrix_[i] = new long long[col_];
        for(int i=0; i<row_; i++)
            for(int j=0; j<col_; j++)
                matrix_[i][j] = (i==j?1:0);
    }
    
    Matrix::Matrix(vector<vector<int>>& v)
    {
        row_ = v.size();
        col_ = v[0].size();
        matrix_ = new long long* [row_];
        for(int i=0; i<row_; i++)
            matrix_[i] = new long long[col_];
        for(int i=0; i<row_; i++)
            for(int j=0; j<col_; j++)
                matrix_[i][j] = v[i][j];
    }
    
    Matrix Matrix::operator * (const Matrix& rh)const
    {
        Matrix result(row_,col_);
        for(int i=0; i<row_; i++)
            for(int j=0; j<col_; j++)
            {
                long long temp=0;
                for(int k=0; k<col_; k++)
                {
                    temp += matrix_[i][k]*rh.matrix_[k][j];
                    temp %= (int)1e9+7;
                }
                result.matrix_[i][j] = temp;
            }
        return result;
    }
    
    Matrix& Matrix::operator = (const Matrix& rh)
    {
        if(this==&rh)
            return (*this);
        for(int i=0; i<col_; i++)
            delete [] matrix_[i];
        delete [] matrix_;
        row_ = rh.row_;
        col_ = rh.col_;
        matrix_ = new long long* [row_];
        for(int i=0; i<row_; i++)
            matrix_[i] = new long long[col_];
        for(int i=0; i<row_; i++)
            for(int j=0; j<col_; j++)
                matrix_[i][j] = rh.matrix_[i][j];
        return (*this);
    }
    
    int Matrix::SumOfAllElements()
    {
        long long result=0;
        for(int i=0; i<row_; i++)
            for(int j=0; j<col_; j++)
            {
                result += matrix_[i][j];
                result %= (int)1e9+7;
            }
        return result;
    }
    Matrix::~Matrix() { for(int i=0; i<col_; i++) delete [] matrix_[i]; delete [] matrix_; } //以上为矩阵类的实现,仅能满足此题方阵乘法,其他的功能性质没有考虑

    class Solution { public: const int mod=1e9+7; int knightDialer(int N) { vector<vector<int> > matrix { {0,0,0,0,1,0,1,0,0,0}, {0,0,0,0,0,0,1,0,1,0}, {0,0,0,0,0,0,0,1,0,1}, {0,0,0,0,1,0,0,0,1,0}, {1,0,0,1,0,0,0,0,0,1}, {0,0,0,0,0,0,0,0,0,0}, {1,1,0,0,0,0,0,1,0,0}, {0,0,1,0,0,0,1,0,0,0}, {0,1,0,1,0,0,0,0,0,0}, {0,0,1,0,1,0,0,0,0,0}, }; Matrix matrix1(matrix); Matrix result(matrix1.GetRow(), matrix1.GetCol()); int step = N-1; while(step>0) { if(step&1) result = result * matrix1; step >>= 1; matrix1 = matrix1 * matrix1; } return result.SumOfAllElements(); } };
  • 相关阅读:
    2019-2020-1 20175323 实验四 外设驱动程序设计
    2019-2020-1 20175323 实验三 并发程序
    2019-2020-1-20175332 20175323 20175228-实验二固件程序设计
    2019-2020-1 20175323 20175332 20175228 实验一开发环境的熟悉
    20175323 团队项目 服务器端函数功能与业务逻辑详解
    2018-2019-2-20175323 java实验五 网络编程与安全
    2018-2019-2-20175323 java实验四 Android程序设计
    2018-2019-2-20175323 java实验三敏捷开发与XP实践
    2018-2019-2-20175323 java实验二《Java面向对象程序设计》
    2018-2019-2 20175323 实验一《Java开发环境的熟悉》实验报告
  • 原文地址:https://www.cnblogs.com/jasonlixuetao/p/10802126.html
Copyright © 2011-2022 走看看