ZOJ 3497 Mistwald (矩阵快速幂解决类似图论问题)


点击打开链接


这道题的题意大概是有一个n*m的图,会告诉你每一个点的出边,与哪四个点相连(可能是重复的),注意这是单向的连接。问从起点(1,1)开始,走P步之后,是一定到终点(n,m),还是可能到,还是不可能到终点?其中在行走的过程中,除了第P步到达的点,其他都不能是终点,但是可以重复经过其他点。


这道题由于方便起见,把起点设置为(0,0),终点就是(n-1,m-1),按行优先遍历,给每个点按顺序编号,然后再做一个邻接矩阵A。A^K的第i行第j列的数代表从编号为i的点走k步是否能到达j。

最关键的一步,就是把终点的出边给删掉。这样就永远不可以从终点出发到另外一个点了。

理解的过程,可以把A^K的计算过程在脑海里模拟一遍,先假设不考虑终点是不是经过,那么新的矩阵的a[i][j]是如何产生的,特别要关注 a[i][k]&&a[k][j] ,k相当于从i到j的一个中间点。


再讨论一下p=0的特殊情况。


#include<cstdio>
#include<cstring>
struct matrix{
int a[30][30];
}base, ans, re;
int n, m;
matrix multiply(matrix u, matrix v)
{
    matrix tmp;
    for (int i = 0; i < n*m; i++)
        for (int j = 0; j < n*m; j++)
        {
            tmp.a[i][j] = 0;
            for (int k = 0; k < n*m; k++)
                if (u.a[i][k] && v.a[k][j])
                {
                    tmp.a[i][j] = 1;
                    break;
                }
        }
    return tmp;
}
void fast_mod(int l)
{
    while (l)
    {
        if (l & 1) ans = multiply(ans, base);
        base = multiply(base, base);
        l >>= 1;
    }
}
int main()
{
    int t, q, p, s;
    int x[5], y[5], num[5];
    scanf("%d", &t);
    getchar();
    while (t--)
    {
        memset(re.a, 0, sizeof(re.a));
        scanf("%d%d", &m, &n);
        getchar();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
            {
                scanf("((%d,%d),(%d,%d),(%d,%d),(%d,%d))", &x[0], &y[0], &x[1], &y[1], &x[2], &y[2], &x[3], &y[3]);
                getchar(); // 输入格式要注意 注意要吃回车
                if (i == (m-1) && j == (n-1)) continue; //最关键的一步
                num[4] = i*n+j;
                for (int k = 0; k < 4; k++) num[k] = (x[k]-1)*n + y[k]-1;
                for (int k = 0; k < 4; k++)
                    re.a[num[4]][num[k]] = 1;
            }
        scanf("%d", &q);
        getchar();
        while (q--)
        {
            scanf("%d", &p);
            getchar();
            if (p == 0)
            {
                if (n == 1 && m == 1) printf("True\n");
                else printf("False\n");
                continue;
            }
            memcpy(base.a, re.a, sizeof(base.a)); // 把一个数组赋值给另外一个数组
            memset(ans.a, 0, sizeof(ans.a));
            for (int i = 0; i < n*m; i++) ans.a[i][i] = 1;
            fast_mod(p);
            s = 0;
            for (int i = 0; i < n*m-1; i++)
                if (ans.a[0][i]) s++;
            if (ans.a[0][n*m-1] == 1)
            {
                if (s == 0) printf("True\n");
                else printf("Maybe\n");
            }
            else printf("False\n");
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值