leetcode_935. Knight Dialer_动态规划_矩阵快速幂
https://leetcode.com/problems/knight-dialer/
在如下图的拨号键盘上,初始在键盘中任意位置,按照国际象棋中骑士(中国象棋中马)的走法走N-1步,能拨出多少种不同的号码。

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

class Solution
{
public:
vector<vector<int> > gra{{,},{,},{,},{,},{,,},
{},{,,},{,},{,},{,}};
const int mod=1e9+;
int knightDialer(int N)
{
int res=;
for(int i=; i<=; i++)
{
vector<vector<int>> dp(N+,vector<int>(,-));
dp[][i]=;
for(int j=;j<=;j++)
res = (res+dfs(N-,j,dp))%mod;
}
return res;
}
int dfs(int step,int num,vector<vector<int>>& dp)
{
if(dp[step][num]>=)
return dp[step][num];
if(step==)
return dp[step][num]=;
int ret=;
for(int i=;i<gra[num].size();i++)
ret = (ret + dfs(step-, gra[num][i], dp))%mod;
return dp[step][num]=ret;
}
};
解法二:动态规划,正向递推

class Solution
{
public:
vector<vector<int> > gra{{,},{,},{,},{,},{,,},
{},{,,},{,},{,},{,}};
const int mod=1e9+;
int knightDialer(int N)
{
int res=;
for(int i=; i<=; i++)
{
vector<vector<int>> dp(N+,vector<int>(,));
dp[][i]=;
for(int j=; j<=N-; j++)
for(int k=; k<=; k++)
for(int l=; l<gra[k].size(); l++)
dp[j][k] = (dp[j][k]+dp[j-][gra[k][l]])%mod;
for(int j=; j<=; j++)
res = (res+dp[N-][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{{,},{,},{,},{,},{,,},
{},{,,},{,},{,},{,}};
const int mod=1e9+;
int knightDialer(int N)
{
int res=;
int dp[][];
//vector<vector<int>> dp(N,vector<int>(10,0));
memset(dp,,sizeof(dp));
for(int i=; i<=; i++)
dp[][i]=;
for(int j=; j<=N-; j++)
for(int k=; k<=; k++)
for(int l=; l<gra[k].size(); l++)
dp[j+][gra[k][l]] = (dp[j+][gra[k][l]]+dp[j][k])%mod;
for(int j=; j<=; j++)
res = (res+dp[N-][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=; i<row_; i++)
matrix_[i] = new long long[col_];
for(int i=; i<row_; i++)
for(int j=; j<col_; j++)
matrix_[i][j] = (i==j?:);
} Matrix::Matrix(vector<vector<int>>& v)
{
row_ = v.size();
col_ = v[].size();
matrix_ = new long long* [row_];
for(int i=; i<row_; i++)
matrix_[i] = new long long[col_];
for(int i=; i<row_; i++)
for(int j=; j<col_; j++)
matrix_[i][j] = v[i][j];
} Matrix Matrix::operator * (const Matrix& rh)const
{
Matrix result(row_,col_);
for(int i=; i<row_; i++)
for(int j=; j<col_; j++)
{
long long temp=;
for(int k=; k<col_; k++)
{
temp += matrix_[i][k]*rh.matrix_[k][j];
temp %= (int)1e9+;
}
result.matrix_[i][j] = temp;
}
return result;
} Matrix& Matrix::operator = (const Matrix& rh)
{
if(this==&rh)
return (*this);
for(int i=; i<col_; i++)
delete [] matrix_[i];
delete [] matrix_;
row_ = rh.row_;
col_ = rh.col_;
matrix_ = new long long* [row_];
for(int i=; i<row_; i++)
matrix_[i] = new long long[col_];
for(int i=; i<row_; i++)
for(int j=; j<col_; j++)
matrix_[i][j] = rh.matrix_[i][j];
return (*this);
} int Matrix::SumOfAllElements()
{
long long result=;
for(int i=; i<row_; i++)
for(int j=; j<col_; j++)
{
result += matrix_[i][j];
result %= (int)1e9+;
}
return result;
}
Matrix::~Matrix()
{
for(int i=; i<col_; i++)
delete [] matrix_[i];
delete [] matrix_;
}
//以上为矩阵类的实现,仅能满足此题方阵乘法,其他的功能性质没有考虑 class Solution
{
public: const int mod=1e9+;
int knightDialer(int N)
{
vector<vector<int> > matrix
{
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
{,,,,,,,,,},
};
Matrix matrix1(matrix);
Matrix result(matrix1.GetRow(), matrix1.GetCol());
int step = N-;
while(step>)
{
if(step&)
result = result * matrix1;
step >>= ;
matrix1 = matrix1 * matrix1;
}
return result.SumOfAllElements();
}
};
leetcode_935. Knight Dialer_动态规划_矩阵快速幂的更多相关文章
- 【CF1151F】Sonya and Informatics(动态规划,矩阵快速幂)
[CF1151F]Sonya and Informatics(动态规划,矩阵快速幂) 题面 CF 题解 考虑一个暴力\(dp\).假设有\(m\)个\(0\),\(n-m\)个\(1\).设\(f[i ...
- 【BZOJ5298】[CQOI2018]交错序列(动态规划,矩阵快速幂)
[BZOJ5298][CQOI2018]交错序列(动态规划,矩阵快速幂) 题面 BZOJ 洛谷 题解 考虑由\(x\)个\(1\)和\(y\)个\(0\)组成的合法串的个数. 显然就是把\(1\)当做 ...
- 【BZOJ4870】组合数问题(动态规划,矩阵快速幂)
[BZOJ4870]组合数问题(动态规划,矩阵快速幂) 题面 BZOJ 洛谷 题解 显然直接算是没法做的.但是要求的东西的和就是从\(nk\)个物品中选出模\(k\)意义下恰好\(r\)个物品的方案数 ...
- 【BZOJ1494】【NOI2007】生成树计数(动态规划,矩阵快速幂)
[BZOJ1494][NOI2007]生成树计数(动态规划,矩阵快速幂) 题面 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为 ...
- CF954F Runner's Problem(动态规划,矩阵快速幂)
CF954F Runner's Problem(动态规划,矩阵快速幂) 题面 CodeForces 翻译: 有一个\(3\times M\)的田野 一开始你在\((1,2)\)位置 如果你在\((i, ...
- nyoj_148_fibonacci数列(二)_矩阵快速幂
fibonacci数列(二) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 In the Fibonacci integer sequence, F0 = 0, F ...
- fibonacci数列(二)_矩阵快速幂
描述 In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn − 1 + Fn − 2 for n ≥ 2. For exampl ...
- 【BZOJ2004】公交线路(动态规划,状态压缩,矩阵快速幂)
[BZOJ2004]公交线路(动态规划,状态压缩,矩阵快速幂) 题面 BZOJ 题解 看到\(k,p\)这么小 不难想到状态压缩 看到\(n\)这么大,不难想到矩阵快速幂 那么,我们来考虑朴素的\(d ...
- 【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)
[BZOJ1009]GT考试(KMP算法,矩阵快速幂,动态规划) 题面 BZOJ 题解 看到这个题目 化简一下题意 长度为\(n\)的,由\(0-9\)组成的字符串中 不含串\(s\)的串的数量有几个 ...
随机推荐
- java类成员访问权限总结(private,default,protected,public)
- 1 model的创建
extJs数据模型之Model博客分类: ExtJs 1 model的创建 //我们利用Ext.define来创建我们的模型类 //DB table person(name,age,email) ...
- FTP两种工作模式:主动模式(Active FTP)和被动模式
在主动模式下,FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,然后开放N+1号端口进行监听,并向服务器发出PORT N+1命令.服务器接收到命令后,会用其本地的FTP数据端口 ...
- (水题)Codeforces - 630H - Benches
https://codeforces.com/problemset/problem/630/H 又一个组合数学的问题,我们先考虑在 $n$ 列中选出 $5$ 列来放椅子,然后对第一列有 $n$ 种放法 ...
- EOJ3263:丽娃河的狼人传说(贪心)
传送门 题意 分析 考虑将区间按右端点排序,再遍历区间,操作即可 建议以加方式写 trick 1.不需要判区间重合 代码 #include<cstdio> #include<cstr ...
- php,c# hamsha1
#!/usr/bin/php <?php print strtoupper(hash_hmac("sha256", "message", "ke ...
- postman接口测试系列:环境配置
最近忙着项目接口测试,经过不同工具的对比,发现postman使用起来挺顺手的,所以马上决定使用这个工具进行接口测试工作.刚开始的时候,了解了下接口测试的相关信息,直接着手编写接口测试的测试用例信息 ...
- deque双向队列
对于双向队列,与队列queue以及vector容器的区别就在于,名字不同,也就是它是双向的,可以从头开始操作,也可以从末尾开始操作. 双向队列的常用方法跟队列queue差不多: 头文件: #inclu ...
- Centos6.8 搭建 Mysql 主从复制
实例环境: MySQL-Master:Centos-6.8:192.168.153.130 MySQL-Slave:Centos-6.8:192.168.153.131 1.两台服务器安装mysql ...
- the little schemer 笔记(1)
第 1 章 玩具 这是原子atom吗?atom是的,因为atom是一个字母a开头的字符串. 这是原子atom吗?turkey是的,因为atom是字母开头的字符串. 这是原子atom吗?1492是的,因 ...
