Description
windy在有向图中迷路了。
该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
Input
第一行包含两个整数,N T。
接下来有 N 行,每行一个长度为 N 的字符串。
第i行第j列为'0'表示从节点i到节点j没有边。
为'1'到'9'表示从节点i到节点j需要耗费的时间。
Output
输出一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。
Sample Input
2 2
11
00
Sample Output
1
Data Constraint
Hint
100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
Solution
对于比较小的n,我们可以直接设f[i][j][k]表示从i走到j用了k步转移只需f[i][j][k]=f[i][l][k-1]*f[l][j][k-1]即可,但是这里步数很大,而点数很小,怎么办?
我们很容易想到用矩阵乘法优化这个转移,我们知道可以用一个二维矩阵表示i到j能否用1的时间走到,那我们将这个矩阵自己相乘k次就可以得到第k秒的方案数,但是这题i到j有些不只是1秒,所以每次矩阵相乘的时候不能更新到下一秒,因此我们要将时间转化为0/1矩阵,我们只要将每个点拆成9个点,然后两两连边,对于i,拆成(i-1)*9+1~(i-1)*9+9对于i到j需要x秒的路径,我们可以转化为从i走到复制的点中的第x-1个向j的1号点连一条时间为1的边,即a[(i-1)*9+x][(j-1)*9+1]=1,然后用初始矩阵(f[i][i]=1)乘以k次转移矩阵就可以得到答案(f[1][(n-1)*9+1])了。
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define I int
#define mem(a,b) memset(a,b,sizeof(a))
#define F(i,a,b) for(I i=a;i<=b;i++)
#define N 100
#define M 2009
using namespace std;
I t,n,a[N][N],f[N][N],g[N][N];
void rd(I &x){
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void mul(){
mem(g,0);
F(i,1,n*9)
F(j,1,n*9)
F(k,1,n*9){
g[i][j]=(g[i][j]+f[i][k]*a[k][j])%M;
}
F(i,1,n*9){
F(j,1,n*9) f[i][j]=g[i][j];
}
}
void tim(){
mem(g,0);
F(i,1,n*9)
F(j,1,n*9)
F(k,1,n*9){
g[i][j]=(g[i][j]+a[i][k]*a[k][j])%M;
}
F(i,1,n*9){
F(j,1,n*9) a[i][j]=g[i][j];
}
}
I main(){
rd(n),rd(t);
mem(a,0);
F(i,1,n*9) f[i][i]=1;
F(i,1,n){
I x=(i-1)*9;
F(j,1,8) a[x+j][x+j+1]=1;
}
F(i,1,n){
F(j,1,n){
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
I x=ch-'0';
if(x) a[(i-1)*9+x][(j-1)*9+1]=1;
}
}
while(t){
if(t&1) mul();
tim();
t>>=1;
}
printf("%d\n",f[1][(n-1)*9+1]);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/98994108