题目描述
POJ 3255
思路
代码
一个没有使用很多编程技巧的方法:
思路是如果有多条最短路,则次短路为最短路,否则次短路。
其中head 数组是用来保存点所对应的边索引(最后一条加入的边),类似头指针,由此可以进入图。
(PS.查资料之后知道了这个是链式前向星,head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.这样在遍历时是倒着遍历的,也就是说与输入顺序是相反的,不过这样不影响结果的正确性.)
如图,0结点对应索引是4的边,其当前点为3,下一边索引为2。以此类推,可以 以0->3->2->1的顺序遍历。
#include <iostream>
#include <cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, head[maxn];
ll cnt[maxn];
ll dis1[maxn], dis2[maxn], dis[maxn];
bool vis[maxn];
struct E{
int v,next,w;
}edge[maxn];
void addEdge(int u,int v,int w){
edge[++k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k;
}
void spfa(int u){
for(int i = 1; i <= n;++i){
dis1[i] = INF;
dis2[i] = INF;
}
memset(vis,0,sizeof(vis));
queue<int>Q;
dis1[u] = 0;
vis[u] = true;
Q.push(u);
while(!Q.empty()){
u = Q.front();
Q.pop();
vis[u]= 0;
for(int i = head[u],v;i != -1,v = edge[i].v;i = edge[i].next){
bool f = 0;
int w = edge[i].w;
if(dis1[v] > dis1[u] + w){
dis2[v] = dis1[v];
dis1[v] = dis1[u] + w;
f = 1;
}
if(dis2[v] > dis1[u] + w && dis1[v] != dis1[u] + w){
dis2[v] = dis1[u] + w;
f = 1;
}
if(dis2[v] > dis2[u] + w){
dis2[v] = dis2[u] + w;
f = 1;
}
if(!f || vis[v])continue;
vis[v] = 1;
Q.push(v);
}
}
}
void spfa2(int u){
for(int i = 1; i <= n;++i)dis[i] = INF;
memset(vis,0,sizeof(vis));
queue<int>Q;
dis[u] = 0;
cnt[u] = 0;
vis[u] = 1;
Q.push(u);
while(!Q.empty()){
u = Q.front();
Q.pop();
vis[u] = 0;
for(int i = head[u]; i != -1;i = edge[i].next){
int v = edge[i].v;
int w = edge[i].w;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!vis[v]){
vis[v] = 1;
Q.push(v);
cnt[v] = cnt[u];
}
else if(dis[v] = dis[u] + w){
cnt[v] += cnt[u];
}
}
}
}
}
int main(){
k = 0;
memset(head,-1,sizeof(head));
memset(cnt,0,sizeof(cnt));
scanf("%d%d",&n,&m);
for(int i = 1; i <= m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
}
spfa(1);
spfa2(1);
if(cnt[n] > 1)printf("%d\n",dis1[n]);
else printf("%d\n",dis2[n]);
return 0;
}