1. 程式人生 > >【[Offer收割]程式設計練習賽13 D】騎士遊歷(矩陣快速冪模板)

【[Offer收割]程式設計練習賽13 D】騎士遊歷(矩陣快速冪模板)

題意:

描述

在8x8的國際象棋棋盤上給定一隻騎士(俗稱“馬”)棋子的位置(R, C),小Hi想知道從(R, C)開始移動N步一共有多少種不同的走法。
輸入

第一行包含三個整數,N,R和C。

對於40%的資料, 1 <= N <= 1000000

對於100%的資料, 1 <= N <= 1000000000 1 <= R, C <= 8
輸出

從(R, C)開始走N步有多少種不同的走法。由於答案可能非常大,你只需要輸出答案模1000000007的餘數。

分析:

對於棋盤中任意一個點(R,C),都有多個位置可跳。

比如:(R,C)可以跳到(R+1,C+2)的位置。

為了描述這種關係,可以建立一個64x64的鄰接矩陣;這個鄰接矩陣第一維對應8X8中的每一個點,第二維維護第一維的點可跳的位置。

對於這樣的一個矩陣,用起始矩陣1X64乘以鄰接矩陣64X64,得到的一個1X64的矩陣,這個矩陣的點上值表示從起始位置走一步,到達該位置有多少中方案。(參考離散數學和線性代數)

因此可以將這個乘以矩陣的過程轉化為矩陣快速冪,迅速求得結果。

程式碼:

#include<bits/stdc++.h>
using namespace std;

typedef vector<unsigned long long> vec;
typedef vector<vec> mat;
const unsigned long long mod = 1000000007;

int dir[8
][2] = { {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1} }; mat mul(mat &a, mat &b) { mat c(a.size(), vec(b[0].size(), 0)); for(int i=0; i<a.size(); ++ i) { for(int j=0; j<b[0].size(); ++ j) { for(int k=0; k<b.size(); ++ k) c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % mod; } } return
c; } mat pow(mat &a, unsigned long long n) { mat b(a.size(), vec(a[0].size(), 0)); for(int i=0; i<a.size(); ++ i) b[i][i] = 1; while(n > 0) { if(n & 1) b = mul(b, a); a = mul(a, a); n >>= 1; } return b; } int main() { int n, r, c; cin >> n >> r >> c; mat a(64, vec(64, 0)); for(int i=0; i<8; ++ i) { for(int j=0; j<8; ++ j) { for(int k=0; k<8; ++ k) { int x = i + dir[k][0], y = j + dir[k][1]; if((x >= 0 && x < 8 && y >= 0 && y < 8) == false) continue; int u = x * 8 + y, v = i * 8 + j; a[u][v] = a[v][u] = 1; } } } mat b(1, vec(64, 0)); b[0][(r-1)*8 + c-1] = 1; mat s = pow(a, n); mat d = mul(b, s); unsigned long long ans = 0; for(auto &i: d) { for(auto &j: i) ans = (ans + j) % mod; } cout << ans << endl; return 0; }