ZOJ 3497 Mistwald

来源:互联网 发布:怎样看b超单上的数据 编辑:程序博客网 时间:2024/06/06 16:48

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


0 0
原创粉丝点击