最短路总结
朴素最短路。
告诉你图的点,和拥有的边,求两点最短路。我们只要把图存到邻接表或者邻接矩阵中,然后用dijkstra或者spfa来操作这个矩阵就可以得到起点到终点的最短路了。
spfa:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #define inf 100000000 using namespace std; int len[102], mp[102][102]; int adjmp[102][102]; int n, m; void Read(int &a){ char ch; a = 0; ch = getchar(); while( !(ch >= '0' && ch <= '9') ) ch = getchar(); while((ch >= '0' && ch <= '9') ){ a = a * 10 + ch - '0'; ch = getchar(); } } void init(){ int i, j; int a, b, c; memset(mp, 0, sizeof(mp)); memset(len, 0, sizeof(len)); for(i=0; i<m; ++i){ Read(a); Read(b); Read(c); adjmp[a][len[a]++] = b; adjmp[b][len[b]++] = a; mp[a][b] = mp[b][a] = c; } } void spfa(){ int i, j, u, v; int MinDis[102]; bool mark[102]; queue<int> Q; memset(mark, 0, sizeof(mark)); for(i=0; i<102; ++i) MinDis[i] = inf; MinDis[1] = 0; Q.push(1); mark[1] = 1; while(!Q.empty()){ u = Q.front(); Q.pop(); for(i=0; i<len[u]; ++i){ v = adjmp[u][i]; if(MinDis[v] > MinDis[u] + mp[u][v]){ MinDis[v] = MinDis[u] + mp[u][v]; if(!mark[v]){ Q.push(v); } } } } printf("%d\n", MinDis[n]); } int main(){ // freopen("c:/aaa.txt", "r", stdin); while(scanf("%d %d", &n, &m)!=EOF){ if(n==0 && m==0) break; init(); spfa(); } return 0; }
dijkstra:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int mp[110][110]; int n, m; const int inf = 999999999; void init(){ int i, j, a, b, c; for(i=1; i<=n; ++i){ for(j=1; j<=n; ++j){ mp[i][j] = inf; } } for(i=1; i<=m; ++i){ scanf("%d %d %d", &a, &b, &c); if(c < mp[a][b]) mp[a][b] = mp[b][a] = c; } } void dijkstra(){ int i, j; int dis[110], min, v, total; bool mark[110]; for(i=1; i<=n; ++i){ dis[i] = mp[1][i]; mark[i] = 0; } dis[1] = 0; mark[1] = 1; total = 0; for(i=1; i<n; ++i){ min = inf; for(j=1; j<=n; ++j){ if(!mark[j] && dis[j] < min){ min = dis[j]; v = j; } } mark[v] = 1; total += min; for(j=1; j<=n; ++j){ if(!mark[j] && dis[j] > min + mp[j][v]){ dis[j] = min + mp[j][v]; } } } printf("%d\n", dis[n]); } int main(){ // freopen("c:/aaa.txt", "r", stdin); while(scanf("%d %d", &n, &m)!=EOF){ if(n==0 && m==0) break; init(); dijkstra(); } return 0; }
变形一:边加一个花费属性,求在满足最短路的情况下,使得花费也最短。
#include <iostream> #include <queue> #include <vector> using namespace std; typedef __int64 lld; struct Node { lld distance, cost; }; lld inf = 0xffffffff; int n, m, s, t; Node f[1005][1005]; vector<int> vec[1005]; lld Mind[1005], Minc[1005]; void spfa() { int i, u, v, sz; bool mark[1050]; queue<int> Q; for( i=1; i<=n; ++i ) { Mind[i] = inf; Minc[i] = inf; mark[i] = 0; } Q.push( s ); mark[s] = 1; Mind[s] = Minc[s] = 0; while( !Q.empty() ) { u = Q.front(); Q.pop(); mark[u] = 0; sz = vec[u].size(); for( i=0; i<sz; ++i ) { v = vec[u][i]; if( Mind[v] > Mind[u] + f[u][v].distance) { Mind[v] = Mind[u] + f[u][v].distance; Minc[v] = Minc[u] + f[u][v].cost; if( !mark[v] ) { mark[v] = 1; Q.push(v); } } else if( Mind[v] == Mind[u] + f[u][v].distance && Minc[v] > Minc[u] + f[u][v].cost ) { Minc[v] = Minc[u] + f[u][v].cost; if( !mark[v] ) { mark[v] = 1; Q.push(v); } } } } } int main() { // freopen( "c:/aaa.txt", "r", stdin); int i, j, a, b, c, d; while( scanf( "%d %d", &n, &m ) == 2 ) { if( n == 0 && m == 0 ) break; for( i=1; i<=n; ++i ) { vec[i].clear(); for( j=1; j<=n; ++j ) { f[i][j].distance = f[i][j].cost = inf; } } for( i=1; i<=m; ++i ) { scanf( "%d %d %d %d", &a, &b, &c, &d ); vec[a].push_back( b ); vec[b].push_back( a ); if( c < f[a][b].distance ) { f[a][b].distance = f[b][a].distance = c; f[a][b].cost = f[b][a].cost = d; } else if( c == f[a][b].distance && d < f[a][b].cost ) f[a][b].cost = f[b][a].cost = d; } scanf( "%d %d", &s, &t ); spfa(); printf( "%I64d %I64d\n", Mind[t], Minc[t] ); } return 0; }
变形二:图中每个点有个权值,边没有权值,求从起点到终点路线中,各点权值累加和最大。并输出路径。
这里涉及到将点的权值转换为边的权值,例如:点a的权值为x,存在一条边e( b -> a ),则边e的权值为a的权值。 然后求最短路。
在求路径时:
如果用Floyd算法的话,用一个二维数组path[][]保存路径,并初始化为指定的空值,在递归输出路径时如果碰到空值,就证明输出结束。具体的做法是这样的,比如我们可以初始化为path[a][b] = -1,经过最短路算法后,path[a][b] = c,表明从a到b中要经过c,而且c->b是最后一步,即a->.....->b->c。
如果用spfa的话,路径的保存用一维的数组就可以了,path[]。path[a] = b,表明有条边b->a,即a的父亲为b。
code :
Floyd
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 105; int n, mp[maxn][maxn], path[maxn][maxn], val[maxn], ca=1; void init() { int i, j, m; scanf( "%d", &n ); for( i=1; i<=n; ++i ) scanf( "%d", &val[i] ); val[++n] = 0; for( i=1; i<=n; ++i ) { for( j=1; j<=n; ++j ) { mp[i][j] = -1; path[i][j] = -1; } } scanf( "%d", &m ); while( m-- ) { int a, b; scanf( "%d %d", &a, &b ); if( mp[a][b] + 1 ) continue; mp[a][b] = val[b]; } } void Print( int s, int e ) { if( path[s][e] + 1 == 0 ) { printf( "%d->%d", s, e ); return; } Print( s, path[s][e] ); printf( "->%d", e == n ? 1 : e ); } void solve() { int i, j, k; for( k=1; k<=n; ++k ) { for( i=1; i<=k; ++i ) { for( j=k; j<=n; ++j ) { if( mp[i][k]+1 && mp[k][j]+1 && mp[i][k]+mp[k][j] > mp[i][j] ) { mp[i][j] = mp[i][k]+mp[k][j]; path[i][j] = k; } } } } if( ca != 1 ) printf( "\n" ); printf( "CASE %d#\npoints : %d\n", ca++, mp[1][n] ); printf( "circuit : " ); Print( 1, n ); printf( "\n" ); } int main() { // freopen( "c:/aaa.txt", "r", stdin ); int T; scanf( "%d", &T ); while( T-- ) { init(); solve(); } return 0; }
spfa:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn = 105; int n, mp[maxn][maxn], val[maxn], ca=1; int path[maxn]; void init() { int i, j, m; scanf( "%d", &n ); for( i=1; i<=n; ++i ) scanf( "%d", &val[i] ); val[++n] = 0; for( i=1; i<=n; ++i ) { for( j=1; j<=n; ++j ) { mp[i][j] = -1; } } scanf( "%d", &m ); while( m-- ) { int a, b; scanf( "%d %d", &a, &b ); if( mp[a][b] + 1 ) continue; mp[a][b] = val[b]; } } void Print( int e ) { if( e == 1 ) { printf( "1" ); return; } Print( path[e] ); printf( "->%d", e == n ? 1 : e ); } void solve() { queue<int> Q; bool mark[maxn]; int Max[maxn], i, u; for( i=1; i<=n; ++i ) { mark[i] = 0; Max[i] = -1; path[i] = i; } Max[1] = 0; Q.push( 1 ); mark[1] = 1; while( !Q.empty() ) { u = Q.front(); Q.pop(); mark[u] = 0; for( i=1; i<=n; ++i ) { if( mp[u][i]+1 && mp[u][i]+Max[u]>Max[i] ) { Max[i] = mp[u][i]+Max[u]; path[i] = u; if( !mark[i] ) { Q.push( i ); mark[i] = 1; } } } } if( ca != 1 ) printf( "\n" ); printf( "CASE %d#\npoints : %d\n", ca++, Max[n] ); printf( "circuit : " ); Print( n ); printf( "\n" ); } int main() { // freopen( "c:/aaa.txt", "r", stdin ); int T; scanf( "%d", &T ); while( T-- ) { init(); solve(); } return 0; }
两个注意的地方:
1.根据求最短路或者最长路,将图的邻接表初始化相应的极限值,求最短路则初始化最大,求最长路初始化为最小指,一般如果不存在负边,我们可以初始化为-1.
2.要考虑是否存在重边的情况。
.