题目描述:
道路千万条,安全第一条!宏帆校区到渝北校区有很多种走法,我们可以把走法看成N个节点的有向图,假设宏帆代表0号节点,渝北代表N-1号节点,GM想从0号节点出发,到N-1号节点,但必须恰好在T时刻到达!你能告诉GM一共有多少种走法吗?注意:GM不能在某个节点逗留,且通过某有向边时严格为给定时间(边权)。
输入:
第一行包含两个整数,N T。 接下来有 N 行,每行一个长度为 N 的字符串。 第i行第j列为'0'表示从节点i到节点j没有边。 为'1'到'9'表示从节点i到节点j需要耗费的时间。
满足 2 <= N <= 10 ; 1 <= T <= 1000000000。
输出:
包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。
输入样例1:
2 2 11 00
输出样例1:
1
输入样例2:
5 30
12045
07105
47805
12024
12345
输出样例2:
852
思路分析:
我们先这样弱化一下题目
假设这个方阵只有0或1,那么其就是不是表示它们点之间的有向边。
而它自己和自己相乘就是表示从一个顶点到另一个顶点。
在T次方之后,矩阵的数就是从i到j的方案数。(其中i为行数,j为列数)。
遗憾的是本题的边权是[0,9],不是[0,1]。
惊喜的是最大边权是9!N也不超过10!都不算大!
马上想到:拆点!把一个点拆成9个点,max:10*9。(当时我连拆点是什么都不知道)。
拆点:
假设有两个节点,边权不超过2的2阶方阵
可以画图为:
由于n和边权都不大,所以想到拆点,把原先的1个点拆成(max边权)个点,所以拆完之后仍然是一个n*(max边权)的方阵!
我们先将单链连接起来,毕竟同点。
然后由于一个规则,原图的i,j点是现图的i.1和j.1点,所以它们的边就是从i.1到j.1的路了。
那么原i到j的路程就是先从i.1到i.边权-1到2.1了。
具体拆分如下:
我们把1.1看成节点1;把1.2看成节点2;把2.1看成节点3;把2.2看成节点4;可得到新矩阵 :
这样我们就可以求出答案。
而新矩阵的行列就是N=n*maxn(maxn为最大边)。
最后一点,答案为B[1][N-maxn+1],这一点就自行思考(实在写不下去了)。
代码实现;
#include<cstdio>
#include<iostream>
#include<cstring>
#define mod 2009
using namespace std;
int n,m,t,maxn;
char a[15][15];
struct node{
int n,m,c[105][105];
node ()
{
memset(c,0,sizeof(c));
}
node operator*(const node &a)
{
node r;
r.n=n;
r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)
r.c[i][j]=(r.c[i][j]+(c[i][k]*a.c[k][j])%mod)%mod;
return r;
}
};
node qkpow(node a,int p)
{
node res;
res.n=res.m=n;
for(int i=1;i<=res.n;i++)
res.c[i][i]=1;
while(p)
{
if(p&1)
res=res*a;
a=a*a;
p/=2;
}
return res;
}
int js(int x,int y)
{
return (x-1)*maxn+y;
}
int main()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=n;i++)
{
scanf("\n");
for(int j=1;j<=n;j++)
{
scanf("%c",&a[i][j]);
maxn=max(a[i][j]-'0',maxn);
}
}
node b;
for(int i=1;i<=n;i++)
{
for(int j=1;j<maxn;j++)
b.c[js(i,j)][js(i,j+1)]=1;
for(int j=1;j<=n;j++)
if(a[i][j]!='0')
b.c[js(i,a[i][j]-'0')][js(j,1)]=1;
}
n*=maxn;
b.n=n;
b.m=n;
b=qkpow(b,t);
printf("%d",b.c[1][n-maxn+1]);
}