http://poj.org/problem?id=3255
题意:
给你一张图,起点为1,终点为n 。求从1到n的次短路。(这里每条边可以重复走)
思路:
两遍最短路算法分别求出1到所有点的最短距离,n到所有点的最短距离。然后枚举每一条边找出第二短路。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x) ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)
#define ll long long
#define inf 0x7f7f7f7f
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 200004
#define N 5007
using namespace std;
//freopen("din.txt","r",stdin);
struct node
{
int v,w;
int next;
}g[M];
int head[N],ct;
int dis1[N],dis2[N];
bool vt[N];
int n,m;
void add(int u,int v, int w)
{
g[ct].v = v;
g[ct].w = w;
g[ct].next = head[u];
head[u] = ct++;
g[ct].v = u;
g[ct].w = w;
g[ct].next = head[v];
head[v] = ct++;
}
void spfa1(int s)
{
int i;
for (i = 0; i < n; ++i)
{
dis1[i] = inf;
vt[i] = false;
}
dis1[s] = 0; vt[s] = true;
queue<int>q;
q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
for (i = head[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
int w = g[i].w;
//printf("%d %d\n",u,v);
if (dis1[v] > dis1[u] + w)
{
dis1[v] = dis1[u] + w;
if (!vt[v])
{
vt[v] = true;
q.push(v);
}
}
}
}
}
void spfa2(int s)
{
int i;
for (i = 0; i < n; ++i)
{
dis2[i] = inf;
vt[i] = false;
}
dis2[s] = 0; vt[s] = true;
queue<int>q;
q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
for (i = head[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
int w = g[i].w;
if (dis2[v] > dis2[u] + w)
{
dis2[v] = dis2[u] + w;
if (!vt[v])
{
vt[v] = true;
q.push(v);
}
}
}
}
}
int main()
{
freopen("din.txt","r",stdin);
int i,j;
int x,y,z;
while (~scanf("%d%d",&n,&m))
{
CL(head,-1); ct = 0;
for (i = 0; i < m; ++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x - 1,y - 1,z);
}
//printf("ZUIDUANLU = %d\n",dis1[n - 1]);
spfa1(0);
//printf("ZUIDUANLU = %d\n",dis1[n - 1]);
spfa2(n - 1);
//printf("%d %d\n",dis1[n - 1],dis2[0]);
int minB = inf;
int minA = inf;
for (i = 0; i < n; ++i)
{
for (j = head[i]; j != -1; j = g[j].next)
{
int v = g[j].v;
int w = g[j].w;
int m = dis1[i] + dis2[v] + w;
int tmp = 0;
//这样枚举才对 minA存的最短路径
if (minA > m)
{
tmp = minA;
minA = m;
m = tmp;
}//minB存的第二短路径
if (minB > m && m != minA)
{
minB = m;
}
//不知道为什么这样枚举就错了.....
/*if (dis1[n - 1] < dis1[i] + dis2[v] + w)
{
disMax = min(disMax,dis1[i] + dis2[v] + w);
}*/
}
}
printf("%d\n",minB);
}
return 0;
}
第二种思路:dis[i][0]表示最短路 dis[i][1]表示次短路 然后一边SPFA 队列里存放来自最短路或者次短路的点的值,然后更新最短路或者次短路。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x) ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)
#define ll long long
#define inf 0x7f7f7f7f
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 200004
#define N 5007
using namespace std;
//freopen("din.txt","r",stdin);
struct node
{
int v,w;
int next;
}g[M];
int head[N],ct;
struct t_node
{
int x,dis;
}p1,p2;
int dis[N][2];
bool vt[N][2];
int n,m;
void add(int u,int v,int w)
{
g[ct].v = v;
g[ct].w = w;
g[ct].next = head[u];
head[u] = ct++;
g[ct].v = u;
g[ct].w = w;
g[ct].next = head[v];
head[v] = ct++;
}
void spfa(int s)
{
int i;
for (i = 1; i <= n; ++i)
{
dis[i][0] = dis[i][1] = inf;
}
dis[s][0] = 0;
queue<t_node>q;
p1.x = s;
p1.dis = 0;
q.push(p1);
while (!q.empty())
{
p2 = q.front(); q.pop();
int u = p2.x;
for (i = head[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
int w = p2.dis + g[i].w;
if (dis[v][0] > w)//更新最短路
{
if (dis[v][0] != inf)//更新次短路
{
dis[v][1] = dis[v][0];
p1.dis = dis[v][1];
p1.x = v;
q.push(p1);
}
dis[v][0] = w;
p1.dis = w;
p1.x = v;
q.push(p1);
}
else if (dis[v][1] > w)//更新次短路
{
dis[v][1] = w;
p1.dis = w;
p1.x = v;
q.push(p1);
}
}
}
}
int main()
{
//freopen("din.txt","r",stdin);
int i;
int x,y,z;
while (~scanf("%d%d",&n,&m))
{
CL(head,-1); ct = 0;
for (i = 0; i < m; ++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
spfa(1);
// printf("<><>%d\n",dis[n][0]);
//for (i = 1; i <= n; ++i)
printf("%d\n",dis[n][1]);
}
return 0;
}
第三种思路:
求第K短路通用算法最短路+A*启发式搜索
(注:以下部份资料来源于网上)
所谓A*就是启发是搜索 说白了就是给搜索一个顺序使得搜索更加合理减少无谓的搜索. 如何来确定搜索的顺序?..也就是用一个值来表示 这个值为f[n]..每次搜索取f[x]最小的拓展 那么这个f[n]=h[n]+g[n]
其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细 点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。
A*算法的估价函数可表示为:
f’(n) = g’(n) + h’(n)
这里,f’(n)是估价函数,g’(n)是起点到终点的最短路径值,h’(n)是n到目标的最短路经的启发值。由 于这个f’(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g’(n),但 g(n)>=g’(n) 才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h’(n),但h(n)<=h’(n)才可(这一点特别的重 要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x) ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)
#define ll long long
#define inf 0x7f7f7f7f
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 200004
#define N 5007
using namespace std;
//freopen("din.txt","r",stdin);
struct node
{
int v,w;
int next;
}g[M];
int head[N],ct;
int dis[N];
bool vt[N];
int n,m;
struct A_node
{
int v,g;
bool operator < (A_node b) const
{
return (g + dis[v]) > (b.g + dis[b.v]);
}
}p1,p2;
void add(int u,int v,int w)
{
g[ct].v = v;
g[ct].w = w;
g[ct].next = head[u];
head[u] = ct++;
g[ct].v = u;
g[ct].w = w;
g[ct].next = head[v];
head[v] = ct++;
}
void spfa(int s)
{
int i;
for (i = 1; i <= n; ++i)
{
dis[i] = inf;
vt[i] =false;
}
dis[s] = 0; vt[s] = true;
queue<int>q;
q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
vt[u] = false;
for (i = head[u]; i != - 1; i = g[i].next)
{
int v = g[i].v;
int w = g[i].w;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if (!vt[v])
{
vt[v] = true;
q.push(v);
}
}
}
}
}
int cnt[N];
priority_queue<A_node> pq;
int A_star(int s,int t,int k)
{
int i;
int ans = 0;
CL(cnt,0);
while (!pq.empty()) pq.pop();
if (s == t) k++;//注意当s==t时需要计算K+1短路,因为s到t这条距离为0的路不能算在这K短路中,这时只需将K++
p1.g = 0; p1.v = s;
pq.push(p1);
while (!pq.empty())
{
p2 = pq.top(); pq.pop();
int u = p2.v;
int gi = p2.g;
cnt[u]++;
if (cnt[u] > k) continue;
if (cnt[t] == k)
{
ans = gi;
break;
}
for (i = head[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
int w = g[i].w;
p1.v = v; p1.g = gi + w;
pq.push(p1);
}
}
return ans;
}
int main()
{
//freopen("din.txt","r",stdin);
int i;
int x,y,z;
while (~scanf("%d%d",&n,&m))
{
CL(head,-1); ct = 0;
for (i = 0; i < m; ++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
spfa(n);
printf("%d\n",A_star(1,n,2));
}
return 0;
}