问题
题目:[jobbu-1447]
思路(dijkstra)
DIJKSTRA( G, w, s ){
1.INITIALIZE-SINGLE-SOURCE(G,s)
2.S = EMPTY
3.Q = V[G];
4.while Q!=EMPTY
4.1 do u = EXTRACT-MIN(Q);
4.2 S = S U {u};
4.3 for each vertex v in adj[u]:
do relax(u,v,w)
}
两个重要数据结构:
dist[i] : shortest path estimate from s to i
path[i]: shortest path from s to i
relax(u,v,w){
if( d[u] + w < d[v] )
d[v] = d[u] + w
}
基本思路:
基于路径长度依次递增的方式。每次从v-s当中找到距离s最近的顶点(注意,prim算法是从v-s中找到距离集合s最近的点,不是单源点s最近的点)。这个点要么是从s可以直接到达,要么是借助集合s当中的节点可以直接到达。
有个地方需要注意,上面的代码求出来的是单源点s到所有点的最短距离。如果只是求出s到t的最短路,那么在将t加入到SET S当中的时候可以判断,满足条件就返回了。
代码
#include <iostream>
#include <vector>
#include <climits>
#include <string.h>
#define N 100 + 8
struct Edge {
int node;
int cost;
Edge() : node(0), cost(0) {}
Edge( int n, int c ) : node(n), cost(c) {}
};
typedef std::vector<Edge> EdgeList;
EdgeList adj_list[N];
int d[N]; // shortest path estimate from s to i;
int flag[N]; // set v , flag[i] = 1 means it is reachable form s to i
int dijk( EdgeList* adj_list, int n, int s, int t );
void relax( int u, int v, int w );
int main(){
int n,m;
while( std::cin >> n >> m ){
if( 0==n && 0==m ) break;
for( int i = 0; i < N; ++i ) adj_list[i].clear();
// input m edges
for( int i = 0; i < m; ++i ){
int u,v,w;
std::cin >> u >> v >> w;
Edge edge(v, w);
adj_list[u].push_back(edge);
Edge edge1( u, w );
adj_list[v].push_back(edge1);
}
// cal the dist
int ans = dijk( adj_list, n, 1, n );
std::cout << ans << std::endl;
}
return 0;
}
int dijk( EdgeList* adj_list, int n, int s, int t ){
for( int i = 1; i <=n ; ++i ){
d[i] = INT_MAX;
}
d[s] = 0;
memset(flag, 0, sizeof(flag));
int cnt = n;
while( cnt-- ){
// select u
int min_dist = INT_MAX;
int u = -1;
for( int i = 1; i <= n; ++i ){
if( flag[i] ) continue;
else{
if( d[i] < min_dist ){
u = i;
min_dist = d[i];
}
}
}
// add u to set s
d[u] = min_dist;
flag[u] = 1;
if( u == t ) return d[u];
// relax (u,v,w)
int sz = adj_list[u].size();
for( int j = 0; j < sz; ++j ){
int v = adj_list[u][j].node;
int w = adj_list[u][j].cost;
relax( u, v, w );
}
}
}
void relax( int u, int v, int w ){
if( d[u] + w < d[v] )
d[v] = d[u] + w;
}
思路(bellman-ford)
我就说一句,bellman-ford遇见负环的情况失败。这是因为存在负环就不存在最短距离。但是它可以求有负边的情形。但是,dijkstra算法没有办法求存在负边的情形。因为他是路径长度依次递增的算法!存在负边与他的思路矛盾。
代码
#include <iostream>
#include <vector>
#include <string.h>
#include <limits.h>
#define N 108
struct Edge{
int node;
int weight;
Edge(){ memset( this, 0, sizeof(Edge) ) ; }
Edge( int n, int w ) : node(n), weight(w) {}
};
typedef std::vector<Edge> EdgeList;
EdgeList adj_list[N];
int d[N];
void init(){
for( int i = 0; i < N; ++i ){
adj_list[i].clear();
}
}
int bellman_ford( int n, int s, int t );
int main( void ){
int n, m;
while( std::cin >> n >> m ){
if( !n && !m ) break;
init();
for( int i = 0; i < m; ++i ) {
int u, v, w;
std::cin >> u >> v >> w;
Edge edge( v, w );
adj_list[u].push_back(edge);
Edge edge1(u, w);
adj_list[v].push_back(edge1);
}
int ans = bellman_ford( n, 1, n );
std::cout << ans << std::endl;
}
return 0;
}
int bellman_ford( int n, int s, int t ){
// init
for( int i = 1; i <= n; ++i ){
d[i] = INT_MAX;
}
d[s] = 0;
// V-1 pass
for( int k = 1; k < n; ++k ){
// relax each edge
for( int u = 1; u <= n; ++u ){
int sz = adj_list[u].size();
for( int j = 0; j < sz; ++j ){
int v = adj_list[u][j].node;
int w = adj_list[u][j].weight;
if( d[u] + w < d[v] )
d[v] = d[u] + w;
}
}
}
// test nagative cycle
for( int u = 1; u <= n; ++u ){
int sz = adj_list[u].size();
for( int j = 0; j < sz; ++j ){
int v = adj_list[u][j].node;
int w = adj_list[u][j].weight;
if( d[u] + w < d[v] )
return -1;
}
}
return d[t];
}