最近真是......zoj很久没切题了,nbut上的几题又超时,阴影啊。。。。>_<
打算按照大牛的训练计划,改动下。先是最短路,参考了算法导论&&一本图论算法。
本质贪心,要求所有边的权值非负。
方法:设置一顶点集合:S,反复选择具有最短路径的顶点u属于V(点数)-s(起点)
并将u加入到S中。对u的所有出边进行松弛操作。
算法导论上对于dijkstra的算法:
dijkstra(G,w,s)
init(G,s)
S<--NULL//顶点集合S
Q<---V[G]//初始化Q,使其包含V中所有顶点。
while(Q!=NULL)
do u <-- EXTRACT-MIN(Q)
S <-- SU{u}
for eachvertex v include to Adj[u]
do relax(u,v,w)
Q为优先队列,以d(即每个点的最短路值)为优先值。体现其贪心。每次都找权值最小的点出队,然后经过该点松弛其他边。
为什么不允许负权边存在??
如图:
权值为7的点在加入集合S后变为6!Dijkstra出错。
关于松弛技术:
实际上就是紧缩上界,松弛一条边的(u,v)的过程中,测试是否可以通过u对迄今找到的v的最短路进行改进。
目的||作用:可以减少最短路径估计得值d[v],并重新更新前驱域pre[v].
时间分析:
依赖于最小优先队列的具体实现。若采用数组实现则为O(V^2+E)
若是稀疏图的情况,利用二叉最小堆来实现时间复杂度为O((V+E)lgV)
实现代码:
#include <iostream>
#include <cstdio>
#define inf 10000000 //无穷大
#define MAXN 20 //顶点个数最大值
int n;
int Edge[MAXN][MAXN];//邻接矩阵
int S[MAXN];//集合S,实际上就是一个标记数组,0表示未加入集合,1表示加入集合
int min[MAXN]; //存取到每个点的最短路长度
int pre[MAXN]; //前驱结点,即父节点
using namespace std;
{
int i,j;
int u,v,w;
cin>>n;//顶点个数n
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(i==j) Edge[i][j]=0
else Edge[i][j]=inf;
}
}
while(cin>>u>>v>>w)
{
if(u==-1&&v==-1&&w==-1) break;
Edge[u][v]=w;
}
Dijsktra(0);//球顶点0到其他顶点的最短路径
int num[MAXN]; //保存最短路上的各顶点的序号
for(i=1;i<n;i++)
{
printf("%d\t",min[i]);
memset(num,0,sizeof(num));
int k=0;//num数组的最后一个元素的下标。
num[k]=i;
while(pre[num[k]]!=0)
{
k++;
num[k]=pre[num[k-1]];
}
k++;
num[k]=0;
for(j=k;j>0;j--)
cout<<num[j]<<"-->";
cout<<num[0]<<endl;
}
return 0;
}
运行结果: