解题报告
题目 :http://poj.org/problem?id=3613
题目大意 :给定一个图G,求恰过K条边的最短路。
题目类型 :floyed + 矩阵快速幂
思路 :离散数学里有著名定理,当原图G的权值仅为0或1时(即联通或不联通)G^k就是每两个点i到j的恰好经过K条边的路。这个题是否也能如此呢?设定状态f[i][j][k]表示从i到j经k条边的最短路,初始条件f[i][j][1] = map[i][j];
àf[i][j][k] = min( f[i][p][k – 1] + map[p][j] )(权值都大于0)
à与foyled类似,由于迭代关系,[k]维可以省略
àf2[i][j] = min( f1[i][p] + map[p][j] ) 由于数据规模太大,还是不够。
à观察矩阵乘法形式temp[i][j] = ∑a[i][j] * b[j][k],发现非常相
à利用矩阵快速幂,求K次 foyled,可以的到解。
为什么是对的呢? 分析一下.
当我们把矩阵乘法里的“相乘”和“求和”改成“相加”和“min()”后,运算仍然满足结合率,即仍然可以使用快速幂的方法。
详见2008. 俞华程《矩阵乘法在信息学中的应用》
注意 :路径长度最大为1000000000.注意最大值初始化。又在这里wa了。
提交情况 :wrong answer 5 次 (INF初始化太小)
Accepted 1 次
经验与收获:很早听说了此题,今天终于过了,虽然是看的解题报告,不过感觉还是不错的。
AC code
#include
#include
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAXI 1010
#define MAXT 110
#define INF 1005000000
intset[MAXI];
struct LINE{
int line[MAXT];
};
struct ROW{
LINE row[MAXT];
};
ROW map;
int point;
voidclear(ROW &temp){
int i, j;
for(i = 0; i < MAXT; i ++)
for(j = 0; j < MAXT; j ++)
temp.row[i].line[j] = INF;
}
ROW Matrix_multi(ROW A, ROW B){
int i, j, k;
ROW temp;
clear(temp);
for(k = 1; k <= point; k ++)
for(i = 1; i <= point; i ++)
for(j = 1; j <= point; j ++)
temp.row[i].line[j] = MIN(temp.row[i].line[j], A.row[i].line[k] + B.row[k].line[j]);
return temp;
}
intGet_ans(int s, int e, int n){
int i, j;
ROW ans;
clear(ans);
for(i = 0; i < MAXT; i ++) ans.row[i].line[i] = 0;
while(n){
if(n & 1) ans = Matrix_multi(ans, map);
map = Matrix_multi(map, map);
n >>= 1;
}
return ans.row[set[s]].line[set[e]];
}
int main(){
int n, t, s, e, x, y, z, i, ans;
scanf("%d %d %d %d", &n, &t, &s, &e);
memset(set, 0, sizeof(set));
clear(map);
point = 0;
for(i = 0; i < t; i ++){
scanf("%d %d %d", &z, &x, &y);
if(!set[x]) set[x] = ++point;
if(!set[y]) set[y] = ++point;
map.row[set[x]].line[set[y]] = z;
map.row[set[y]].line[set[x]] = z;
}
ans = Get_ans(s, e, n);
printf("%d\n", ans);
return 0;
}