题目概述
有N个地点,R条双向路,求次短路长度
时限
2000ms/6000ms
输入
第一行整数N,R,其后R行,每行三个整数,描述一条路连接的两个地点及其长度,输入只有一组
限制
1<=N<=5000;1<=R<=100000
输出
一个数,为所求次短路长度
样例输入
4 4
1 2 100
2 4 200
2 3 250
3 4 100
样例输出
450
讨论
图论,第K短路,bellman_ford队列优化+A*算法,前面在poj 2449用过一次,这里相当于简化了一下,这里的A*是基于一个基本公式求得次短路的,f=g+h,h是从终点到某个点a的最短路,g是从起点到点a的最短路,这样f的意义就是从起点经过点a到终点的最短路,这样的路有不少,将所有这样的路按f的大小升序排列,第一条就是平日里常见的最短路,第二条就是题目要求的次短路
从实现方面说,首先将所有路的方向反转(当然这个题本来就是双向路),利用bellman_ford算出终点到所有点的最短距离,也就是h,然后A*从起点开始,把和遇到的点相连的点都加入优先队,利用优先队对f进行排序,第一次遇到终点得到最短路,第二次就是次短路了,然后输出f(其实g也一样,毕竟终点到终点距离是0,h是0)
题解状态
2644K,172MS,C++,1389B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 5003
#define memset0(a) memset(a,0,sizeof(a))
int N, R;//地点总数 路径总数
int to[200003], w[200003], pre[200003], al[MAXN], index = 1, dis[MAXN];//邻接表的4数组1变量 to 路的终点 weight 权值 previous 同起点上一条路的下标 adjacency_list 每个起点的最后一条路下标 index 分配路下标的变量 前面的数组大小是因为100000*2 双向路要双倍 distance 最短路长度
queue<int>q;
bool inq[MAXN];
struct It//A*的路的结构
{
int to, g;//某个中间节点 起点到点to的最短距离
bool operator<(const It &b)const
{
return g + dis[to]>b.g + dis[b.to];//按f降序排列 放到优先队 队头是f最小的
}
};
priority_queue<It>pq;//A*的优先队
int fun()
{
for (int p = 0; p < R; p++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);//input
to[index] = b, w[index] = c, pre[index] = al[a], al[a] = index++;
to[index] = a, w[index] = c, pre[index] = al[b], al[b] = index++;
}
for (int p = 1; p <= N; p++)//下面是bellman_ford 从N开始反向求最短路长度 即每个点的h
dis[p] = INF;
dis[N] = 0;
q.push(N);
while (!q.empty()) {
int a = q.front();
q.pop();
inq[a] = 0;
for (int p = al[a]; p; p = pre[p]) {
if (dis[to[p]]>dis[a] + w[p]) {
dis[to[p]] = dis[a] + w[p];
if (!inq[to[p]]) {
q.push(to[p]);
inq[to[p]] = 1;
}
}
}
}
int cnt = 0;//已经遇到终点的次数 当第二次遇到时就是次短路
It a, b;//为避免每次都构造而提前声明
a.to = 1, a.g = 0;
pq.push(a);//压入起点
while (!pq.empty()) {
a = pq.top();
pq.pop();
if (a.to == N)//当遇到终点时
if (++cnt > 1 && a.g > dis[1])//第二个判断是从poj 2449搬来的 可能并无实际意义 只是防止起点就是终点
return a.g;
for (int p = al[a.to]; p; p = pre[p]) {
b.to = to[p], b.g = a.g + w[p];//计算出邻接点的属性
pq.push(b);//无论长短都压入优先队
}
}
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
scanf("%d%d", &N, &R);//input
printf("%d\n", fun());//output
}
EOF