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(); } };

 

posted on 2019-05-02 13:15  JASONlee3  阅读(377)  评论(0编辑  收藏  举报

导航