【矩阵乘法】【SCOI2009】迷路

【问题描述】
windy在有向图中迷路了。
该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

【输入格式】
输入文件road.in第一行包含两个整数,N T。
接下来有 N 行,每行一个长度为 N 的字符串。
第i行第j列为'0'表示从节点i到节点j没有边。
为'1'到'9'表示从节点i到节点j需要耗费的时间。

【输出格式】
输出文件road.out包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

【输入样例一】
2 2
11
00

【输出样例一】
1

【样例解释一】
0->0->1

【输入样例二】
5 30
12045
07105
47805
12024
12345

【输出样例二】
852

【数据规模和约定】
30%的数据,满足 2 <= N <= 5 ; 1 <= T <= 30 。
100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
此题考察矩阵乘法。

首先注意到每条边的权值取值范围为[0, 10),所以拆点,将每个点拆成9个点,即将点i拆为[9i, 9i+9]9个点,并且对于每个t ∈[9i, 9i+9)都从t到t+1连一条权值为1的边。

读入数据,若其中i到j有边,且权值为t(t > 0,这个非常重要!),则从9i+t-1到9j连一条权值为1的边(这样做的目的是保证从i到j经时间t)。

求以上所得矩阵的T次方,并输出0到9n-9的权值即可。

提交情况:提交2次。
第一次30分,WrongAnswer * 7;(乘法取余出错。)
第二次100分。
ACCode:

#include <cstdio>
#include <cstring>
#include <cstdlib>

const char fi[] = "road.in";
const char fo[] = "road.out";
const int maxR = 20;
const int maxN = 210;
const int MOD = 2009;
const int maxT = 9;

typedef int Matrix[maxN][maxN];

int tmp[maxR][maxR];
Matrix mp = {0};
int n, T;

void init_file()
{
	freopen(fi, "r", stdin);
	freopen(fo, "w", stdout);
	return;
}

void readdata()
{
    scanf("%d%d", &n, &T);
    for (int i = 0; i < n; ++i)
    {
        getchar();
        for (int j = 0; j < n; ++j)
            tmp[i][j] = getchar() - '0';
    }
    for (int i = 0; i < n; ++i)
    for (int k = 1; k < maxT; ++k)
        mp[i * maxT + k - 1][i * maxT + k] = 1;
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    if (tmp[i][j]) //有边才能连边,否则无意义且出错。
        mp[i * maxT + tmp[i][j] - 1][j * maxT] = 1;
    n *= maxT;
    return;
}

void Mul(Matrix &a, Matrix b)
{
    Matrix ans = {0};
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    for (int k = 0; k < n; ++k)
        (ans[i][j] += a[i][k] * b[k][j]) %= MOD;
	//这里应注意容易出错。
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
        a[i][j] = ans[i][j];
    return;
}

void Power(Matrix &mp, int T)
{
    Matrix ans, tmp;
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    {
        ans[i][j] = 0;
        tmp[i][j] = mp[i][j] % MOD;
    }
    for (int i = 0; i < n; ++i)
        ans[i][i] = 1;
    for (; T; T >>= 1)
    {
        if (T & 1) Mul(ans, tmp);
        Mul(tmp, tmp);
    }
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
        mp[i][j] = ans[i][j] % MOD;
    return;
}

void work()
{
    Power(mp, T);
    printf("%d\n", mp[0][n - maxT]);
    return;
}

int main()
{
	init_file();
	readdata();
	work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值