Silver Cow Party问题
题目
One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1…N is going to attend the big cow party to be held at farm #X (1 ≤ X ≤ N). A total of M (1 ≤ M ≤ 100,000) unidirectional (one-way roads connects pairs of farms; road i requires Ti (1 ≤ Ti ≤ 100) units of time to traverse.
Each cow must walk to the party and, when the party is over, return to her farm. Each cow is lazy and thus picks an optimal route with the shortest time. A cow’s return route might be different from her original route to the party since roads are one-way.
Of all the cows, what is the longest amount of time a cow must spend walking to the party and back?
Input
Line 1: Three space-separated integers, respectively: N, M, and X
Lines 2… M+1: Line i+1 describes road i with three space-separated integers: Ai, Bi, and Ti. The described road runs from farm Ai to farm Bi, requiring Ti time units to traverse.
Output
Line 1: One integer: the maximum of time any one cow must walk.
Sample Input
4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3
Sample Output
10
Hint
Cow 4 proceeds directly to the party (3 units) and returns via farms 1 and 3 (7 units), for a total of 10 time units.
初步探索算法思路
刚接触这道题时,第一反映是使用多源最短路径的Floyd算法,在最短路的二维数组中寻找哪个点到目标点的距离最长,但是时间复杂度为O(n3),明显会超时,因此需要考虑其他算法。
正确算法思路
我们需要将这个题进行分解成两个过程:
1、前往:首先求出由其他各点到目标点x的最短路径(这个过程为每头奶牛从农场出发到达目标农场的最短路径)
2、回家:每头奶牛回家的过程,即为从目标点x回到其他各点的过程(这个过程为从目标农场出发到达每头奶牛出发时的农场)
第一阶段“前往”,可以使用Dijkstra算法或者Bellman_Ford算法(这里我使用Dijkstra算法
第二阶段“回家”,我们换个思路,从目标农场回到家中,即反向从家中到达目标农场,只是此时每两个农场间的路进行交换,即将各条单向路径的方向调转,也就是本来从农场1到农场2的路径,变成农场2到农场1的路径。然后再次使用第一阶段的算法再求一次各点最短路径
最后将两个阶段的最短路径加和后从中选择最长的路径即为本题解
解题代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 99999999 //设置无穷大
using namespace std;
int N,M,X;
int e[10005][10005]; //带权值的邻接矩阵
int book[10005]; //记录该节点是否为已为最短路径
int dis[10005]; //总最短路径
int dis1[10005]; //第一阶段最短路径
int dis2[10005]; //第二阶段最短路径
//邻接矩阵初始化函数
int enit()
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(i==j)
e[i][j]=0;
else
e[i][j]=INF;
}
//Dijkstra算法函数
void Dijkstra(int dis[])
{
memset(book,0,sizeof(book)); //将book数组中所有元素置为0
for(int i=1;i<=N;i++) //dis数组初始化
dis[i]=INF;
book[X]=1; //出发农场的必为最短距离且距离为0
//首先初始化点X到所有点的最短距离为X与此点的邻接边
for(int i=1;i<=N;i++)
dis[i]=e[X][i];
//从最短路径数组中每次选择最小路径权值的节点,可以保证每次更新路径最终为最短路径
for(int i=1;i<=N;i++){
int min=INF;
int mark;
for(int j=1;j<=N;j++)
if(book[j]==0&&min>dis[j]){
min=dis[j];
mark=j;
}
book[mark]=1; //找到最小权值的节点,并认为其已为最短路径
//用最小权值节点去更新其他节点的最短路径
for(int j=1;j<=N;j++)
if(e[mark][j]<INF)
if(dis[j]>dis[mark]+e[mark][j])
dis[j]=dis[mark]+e[mark][j];
}
}
//转置邻接矩阵函数
int exchange()
{
for(int i=1;i<=N;i++)
for(int j=i+1;j<=N;j++)
swap(e[i][j],e[j][i]);
}
int main()
{
scanf("%d %d %d",&N,&M,&X);
enit();
//输入数据,并存入邻接矩阵
int from,to,cost;
for(int i=0;i<M;i++){
scanf("%d %d %d",&from,&to,&cost);
e[from][to]=cost;
}
Dijkstra(dis1); //未转置前调用一次
exchange(); //转置邻接矩阵
Dijkstra(dis2); //转置后调用一次
//将两次最短路径结果加和
for(int i=1;i<=N;i++)
dis[i]=dis1[i]+dis2[i];
//寻找最终最短路径数组中的最大权值
int max=-1;
for(int i=1;i<=N;i++)
if(max<dis[i])
max=dis[i];
printf("%d",max);
return 0;
}