网上看的大多是转置矩阵,然后套用两次dijkstra算法
个人认为,转置一个n*n的矩阵在时间方面是很不划算的,虽然dijkstra算法本身的复杂度就是O(n^2)(当然你能用堆优化达到O((n + m) * log n),用斐波那契堆优化可以达到O(m + n * log n))……但是个人认为这道题完全没有必要转置这个矩阵。
当牛去第x个牧场的时候,我们不太习惯这种模式,一般都是单源多终点的题,这次来个多元单终点的。。。其实算法是一样的,就是看对Dijstra算法的理解度。
dijkstra算法就是不断维护并更新dist[n]的最小值,那么(我个人比较偏向于tab缩进,tab宽度=4):
#include <cstdio>
#include <cstring>
#include <climits>
#define MAXN 1001 // 这里可以用1000,但是为了保险还是给个1001吧
unsigned to_dist[MAXN]; // 牛要过去时的dist[n]
unsigned back_dist[MAXN]; // 牛回来时的dist[n]
unsigned mp[MAXN][MAXN];
bool s[MAXN];
int main(int argc, char* argv[])
{
int n, m, x, i, j, a, b, t, k; // n,m,x都是题目给的,i,j是临时变量(我懒得每for就临时变量)
unsigned mins;
while (scanf("%d%d%d", &n, &m, &x) != EOF)
{
memset(mp, 0xff, sizeof(mp)); // 默认边长为0xffffffff,unsigned int的最大值
memset( s, 0, n); // 这里之所以是n是因为我们知道bool占一个字节
while (m-- && scanf("%d%d%d", &a, &b, &t))
mp[a - 1][b - 1] = t;
mp[x - 1][x - 1] = 0;
// 先做不常见的倒过来行走
for (i = 0; i < n; ++i)
to_dist[i] = mp[i][x - 1]; // 倒过来赋dist[n]的值即可,从每个起点到x(即数组里的x-1)
s[x - 1] = true;
for (i = 1; i < n; ++i) // 这里可以使用while(1),因为在运行了n-1次后必定终止
{
mins = UINT_MAX;
k = -1;
for (j = 0; j < n; ++j)
{
if (!s[j] && mins > to_dist[j])
{
mins = to_dist[j];
k = j;
}
}
if (k == -1)
break;
s[k] = true;
for (j = 0; j < n; ++j)
{
// 倒过来思考即可,如果从j到k有路而且dist[j]比从j到k再通过k到x的路径要长,就更新它
if (!s[j] && mp[j][k] != UINT_MAX && to_dist[j] > mins + mp[j][k])
to_dist[j] = mins + mp[j][k];
}
}
memset(s, 0, n); // 计算返回的dist[n]前更新s[n],下面都是套公式,不解释了
for (i = 0; i < n; ++i)
back_dist[i] = mp[x - 1][i];
s[x - 1] = true;
for (i = 1; i < n; ++i)
{
mins = UINT_MAX;
k = -1;
for (j = 0; j < n; ++j)
{
if (!s[j] && mins > back_dist[j])
{
mins = back_dist[j];
k = j;
}
}
if (k == -1)
break;
s[k] = true;
for (j = 0; j < n; ++j)
{
if (!s[j] && mp[k][j] != UINT_MAX && back_dist[j] > mins + mp[k][j])
back_dist[j] = mins + mp[k][j];
}
}
mins = 0;
for (i = 0; i < n; ++i)
{
if (mins < to_dist[i] + back_dist[i])
mins = to_dist[i] + back_dist[i];
}
printf("%u\n", mins); // 注意这里的输出格式,不然WA别怪我
}
return 0;
}