ZOJ 3497 Mistwald

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值