HihoCoder-1504 骑士游历
思路:
此类搜索一般dfs。但是这里的数据量太大,显然会造成树很深,所以不便于深搜的展开。从(R,C)走N步都多少种走法,可以理解为从(R,C)走N步到(1,1)-(8,8)走法的和,显然运用到了离散数学中邻接矩阵的知识。_map[i][j]=x表示从i结点到j结点走一步可到达有x种方法,_map[i][j]^n=x表示走n步有x种方法可以从i走到j。(具体了解离散数学,再次简述,假设矩阵相乘_map[i][j]^m _map[i][j]^n,那么对于_map[1][2]^(m+n)时由_map[1][k]*_map[k][2]逐个相加,可以这样理解,_map[1][k]表示从1走到k有x种方法,_map[k][2]表示从k走到2有y种方法,那么经过k结点从1到2就有x*y种方法,遍历每一个点当中间结点,那么最后也就是矩阵相乘的结果).
邻接矩阵:
_map[i][j]表示从第i个点到第j个点可不可到,(1,1)-(8,8)逐个编号0-63;
知识点:
1.矩阵乘法代码实现。
2.快速幂的矩阵用法。
3.取模注意事项,只要牵扯四则运算 就取模。
4.函数返回二维数组指针较复杂,可以用结构体封装起来返回结构体,较为简单。
代码实现:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007; //需要的取模大小
int dir[8][2] = {{1,2},{2,1},{-1,2},{1,-2},{2,-1},{-2,1},{-1,-2},{-2,-1}};//用于初始化矩阵可以到达的位置。
typedef struct {//封装矩阵
ll _map[100][100];
}mat;
ll r,c;
mat init(mat a){//初始化邻接矩阵
for(int i = 0;i < 8;i++){
for(int j = 0;j < 8;j++){
for(int k = 0;k < 8;k++){
int tx = i+dir[k][0];
int ty = j+dir[k][1];
if(tx < 0||ty < 0||tx >= 8||ty >= 8) continue;
a._map[i*8+j][tx*8+ty] = 1;
}
}
}
return a;
}
mat mul(mat a,mat b){
mat c;
memset(c._map,0,sizeof(c._map));
for(int i = 0;i < 64;i++){
for(int j = 0;j < 64;j++){
for(int k = 0;k < 64;k++){
c._map[i][j] += (a._map[i][k]*b._map[k][j])%mod;
c._map[i][j]%=mod;//注意此处取模,上面未对c取模
}
}
}
return c;
}
void qpow(mat a,ll n){
mat ans;
memset(ans._map,0,sizeof(ans._map));
for(int i = 0;i < 64;i++)//注意:普通快速幂ans=1,在矩阵中初始化为单位阵(即矩阵的单位元)
ans._map[i][i] = 1;
while(n){
if(n % 2 == 1) {
ans = mul(ans,a);
}
a = mul(a,a);
n/=2;
}
int s = (r-1)*8+(c-1);//因为我们矩阵从0开始的,所以需要减一(题目中从1开始)
int ans1 = 0;
for(int i = 0;i < 64;i++){//遍历每一个点,看从(R,C)到该点有多少种路径
ans1 = (ans1+ans._map[s][i])%mod;
}
cout << ans1 << endl;
}
int main(){
mat m;
ll n;
m = init(m);
cin >> n >> r >> c;
qpow(m,n);
return 0;
}