以下选自:matrix67 十个利用矩阵乘法解决的经典问题
经典题目8 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数modp的值
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。
由上可得ans = A^ A ans[i][j]是从i到j经过两条边枚举所有情况可得,联想到floyd算法类似矩阵乘法的形式,可以将矩阵乘法的部分改为松驰边的操作,便解决这问题
这里本来用递归实现矩阵乘法,结果栈爆了,只好用循环模拟递归。
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; struct MATRIX { int m[250][250]; }; MATRIX mul(MATRIX num1, MATRIX num2, int n) { int i,j,k; MATRIX res; memset(&res, 63, sizeof(res)); for(k = 1; k <= n; k++)//这行调到最外循环也可以 for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { { if(num1.m[i][k] + num2.m[k][j] < res.m[i][j]) { res.m[i][j] = num1.m[i][k] + num2.m[k][j]; } } } } return res; } MATRIX POW(MATRIX map,int n,int k) { /* MATRIX first; memcpy(&first, &map, sizeof(map)); if(k ==1) { return map; } MATRIX res; int sta[50]; int cnt = 0; while(k) { if(k % 2 == 1) { //sta.push(1); sta[cnt++] = 1; } else { //sta.push(0); sta[cnt++] = 0; } k = (k >> 1); }//为方便模拟递归 http://blog.csdn.net/paul08colin/article/details/7411506 cnt--;//去掉第一个栈顶元素 while(cnt != 0) { if(sta[--cnt]== 0) { res = mul(map,map,n); memcpy(&map,&res,sizeof(res)); } else { res = mul(map,map,n); res = mul(res,first,n); memcpy(&map,&res,sizeof(res)); } }*/ //////上面注释部分是方法1,模拟递归。效率会相对低些 MATRIX res; int i; bool flag = true; while(k) { if( k & 1) { if(flag == true) { memcpy(&res,&map,sizeof(map)); flag = false; } else { res = mul(res,map,n); } } map = mul(map,map,n); k>>=1; } // return res; return res; ///上面是用二进制的思想,效率较快 } int main() { int k,n,s,t; while(scanf("%d %d %d %d", &k, &n, &s, &t) != EOF) { int i; int f[1010]; memset(f,0,sizeof(f)); MATRIX map; memset(&map,63,sizeof(map)); //printf("%d\n",map.m[0][0]); int from,to,len; int MAX = 1; for(i = 0; i < n; i++) { scanf("%d %d %d",&len, &from, &to); if(f[from] == 0) { f[from] = MAX++; } if(f[to] == 0) { f[to] = MAX++; } map.m[f[from]][f[to] ] = len; map.m[f[to]][f[from]] = len; } MAX--; MATRIX res; res = POW(map,MAX,k); printf("%d\n",res.m[f[s]][f[t]]); } return 0; }