概述
【题意】
给定一个含T条边的无向图,求从s到e的恰好经过n条边的最短路(可以经过重复边)。
【题解】
我们先来回想一下floyd。它的本质实际上是DP。
设 f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]为从i开始,经过1~k的某些点到达j的最短路的总权值,则:
f [ k ] [ i ] [ j ] = m i n { f [ k − 1 ] [ i ] [ k ] + f [ k − 1 ] [ k ] [ j ] } f[k][i][j]=min{f[k-1][i][k]+f[k-1][k][j]} f[k][i][j]=min{f[k−1][i][k]+f[k−1][k][j]}
降维可得:
f [ i ] [ j ] = m i n { f [ i ] [ k ] + f [ k ] [ j ] } f[i][j]=min{f[i][k]+f[k][j]} f[i][j]=min{f[i][k]+f[k][j]}
同时,这也是为什么要先枚举k的原因。
那么,按照这个思路,我们将它改一改。设
f
[
t
]
[
i
]
[
j
]
f[t][i][j]
f[t][i][j]为从
i
i
i到
j
j
j经过
t
t
t个中间点的最短路,
G
[
i
]
[
j
]
G[i][j]
G[i][j]为图初始的邻接矩阵。则:
f
[
t
]
[
i
]
[
j
]
=
m
i
n
{
f
[
t
−
1
]
[
i
]
[
k
]
+
g
[
k
]
[
j
]
}
f[t][i][j]=min{f[t-1][i][k]+g[k][j]}
f[t][i][j]=min{f[t−1][i][k]+g[k][j]}
目测要T。接下来考虑优化。
我们先把
f
f
f的第一维去掉,而是用多个数组来表示不同状态下的
f
[
i
]
[
j
]
f[i][j]
f[i][j],记作
d
t
[
i
]
[
j
]
d_t[i][j]
dt[i][j]。则原方程可转化为:
d
t
[
i
]
[
j
]
=
m
i
n
{
d
t
−
1
[
i
]
[
k
]
+
g
[
k
]
[
j
]
}
d_t[i][j]=min{d_{t-1}[i][k]+g[k][j]}
dt[i][j]=min{dt−1[i][k]+g[k][j]}
再想一想,我们还可以把它改写成下列形式:
d
t
[
i
]
[
j
]
=
m
i
n
{
d
t
′
[
i
]
[
k
]
+
d
t
−
t
′
[
k
]
[
j
]
}
d_t[i][j]=min{d_{t'}[i][k]+d_{t-t'}[k][j]}
dt[i][j]=min{dt′[i][k]+dt−t′[k][j]}
我们发现,其实这个方程的转移是满足结合律的。所以,在优化的时候,我们可以像矩阵快速幂一样,对方程的转移进行二进制优化。
仿照矩阵快速幂,我们可以像这样定义新乘法:
#define ll long long
struct matrix{
ll a[max_n][max_n];
matrix(){memset(a, 0x3f, sizeof a);}
matrix operator *(const matrix b) const
{
matrix c;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
for(int k = 1; k <= n; k++)
c.a[i][j] = min(c.a[i][j], a[i][k] + b.a[k][j];
return c;
}
};
在这篇博客里提到:
G n [ i ] [ j ] G^n[i][j] Gn[i][j]表示从i走n步到j的方案数
于是,我们对原邻接矩阵做n次上面的奇怪的乘法,就可以得到答案。
由于边数顶破天100条而编号可能很大,所以我们可以对点的编号离散化。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mn = 205, mm = 1005;
int cnt;
struct matrix{
int a[mn][mn];
matrix() {memset(a, 0x3f, sizeof a);}
matrix operator* (const matrix x) const
{
matrix c;
for(int i = 1; i <= cnt; i++)
for(int j = 1; j <= cnt; j++)
for(int k = 1; k <= cnt; k++)
c.a[i][j] = min(c.a[i][j], a[i][k] + x.a[k][j]);
return c;
}
}ret, h;
struct edge{
int a, b, w;
}e[mm];
bool vis[mm];
int val[mm];
inline void ksm(int m)
{
while(m)
{
if(m & 1)
ret = ret * h;
h = h * h, m >>= 1;
}
}
int main()
{
int n, m, s, t, i;
scanf("%d%d%d%d", &m, &n, &s, &t);
for(i = 1; i <= n; i++)
scanf("%d%d%d", &e[i].w, &e[i].a, &e[i].b), vis[e[i].a] = vis[e[i].b] = 1;
for(i = 1; i <= 1000; i++)
if(vis[i])
val[i] = ++cnt;
for(i = 1; i <= n; i++)
{
int a = val[e[i].a], b = val[e[i].b];
h.a[a][b] = h.a[b][a] = e[i].w;
}
for(i = 1; i <= cnt; i++)
ret.a[i][i] = 0; //这里最开始的矩阵的0次方表示从i至j走0步的最短路,所以除了ret.a[i][i]=0外全部取正无穷
ksm(m);
printf("%dn", ret.a[val[s]][val[t]]);
}
最后
以上就是高兴大树为你收集整理的【poj3613】Cow Relays的全部内容,希望文章能够帮你解决【poj3613】Cow Relays所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复