題目描述:
道路千萬條,安全第一條!宏帆校區到渝北校區有很多種走法,我們可以把走法看成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]);
}