http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3497
题意 : 给你一个5 * 5的矩阵, 存在一些单向边(存在自环), 然后问你从 (1, 1) -> (n, m)走k步到达 这一情况是否存在。
思路 :先把每个点重新标号为 1, 2, 3 ... n * m, 然后构造成(n * m) * (n * m) 的矩阵, 最后用矩阵乘法做。
比如 : G[i][j][k] : 表示从i -> j 走k步的路线数目。
则 G[i][j][t1 + t2] = ∑( G[i][k][t1] * G[k][j][t2] )其中k >= 1 && k < n * m 并且i != (n * m)注 : 题中提到从(n, m)这个点不能再走到其它点的。
观察式子可以发现其实就是矩阵乘法。 所以这题可以转换为矩阵乘法判断了。 注意别TLE。
PS : 比赛的时候没想出, 后来发现这其实就类似这一题 (这题以前是肉鸽教我的, 当时只当是矩阵快速幂在做, 并没有深入了解其含义)。
推荐一篇和矩阵有关的好文章 : http://www.matrix67.com/blog/archives/276
然后么,我对于Maybe和True的理解可能有点问题, 不过A了。贴下代码。
CODE :
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 35;
struct mat {
int a[maxn][maxn];
}bit[30], G;
int n, N, M;
mat mul(mat a, mat b) {
mat ans;
for (int i = 1; i < n; i++) {
for (int j = 1; j <= n; j++) {
ans.a[i][j] = 0;
for (int k = 1; k < n && !ans.a[i][j]; k++) {
ans.a[i][j] += a.a[i][k] * b.a[k][j];
}
if (ans.a[i][j] >= 2)ans.a[i][j] = 1;
}
}
return ans;
}
void init(mat & a) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)a.a[i][j] = 0;
}
}
void read(int u) {
char ss[maxn];
scanf("%s", ss);
int m = strlen(ss), cnt = 0, aa[maxn];
for (int i = 0; i < m; i++) {
if (ss[i] >= '0' && ss[i] <= '9') {
aa[++cnt] = ss[i] - '0';
}
}
for (int i = 1; i <= cnt; i += 2) {
if (u != n)G.a[u][(aa[i] - 1) * M + aa[i+1]] = 1;
}
}
void work() {
bit[1] = G;
for (int i = 2; i <= 29; i++) {
bit[i] = mul(bit[i-1], bit[i-1]);
}
}
void show(mat a) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)printf("%d", a.a[i][j]);
printf("\n");
}
}
int isok(int x) {
int c = 1;
mat res;
init(res);
for (int i = 1; i <= n; i++)res.a[i][i] = 1;
while (x) {
if (x & 1) res = mul(res, bit[c]);
c++; x >>= 1;
}
return res.a[1][n];
}
int main() {
int T;
while (scanf("%d", &T) != EOF) {
while (T--) {
scanf("%d%d", &N, &M);
n = N * M;
init(G);
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++) {
read((i - 1) * M + j);
}
}
work();
int Q, num = 0;
for (int i = 0; i <= n * n; i++) {
num += isok(i);
}
scanf("%d", &Q);
while (Q--) {
int q; scanf("%d", &q);
bool ok = isok(q);
if (!ok) {
printf("False\n");
}else {
if (num >= 2)printf("Maybe\n");
else printf("True\n");
}
}
printf("\n");
}
}
return 0;
}