http://acm.hdu.edu.cn/showproblem.php?pid=1385
求最短路,要求输出字典序最小的路径。
spfa:拿一个pre[]记录前驱,不同的是在松弛的时候,要考虑和当前点的dis值相等的情况,解决的办法是dfs找出两条路径中字典序较小的,pre[]去更新。把路径当做字符串处理。
我只用之前的pre去更新当前点,并没考虑到起点到当前点的整个路径,其实这样并不能保证是字典序最小。wa了N次,于是乎搜了下题解,发现用spfa解的很少,看到了某大牛的解法如上,感觉很赞的想法。
#include <stdio.h> #include <algorithm> #include <set> #include <map> #include <vector> #include <math.h> #include <string.h> #include <queue> #define LL long long #define _LL __int64 using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 110; int n; int tax[maxn]; int Map[maxn][maxn]; int dis[maxn],inque[maxn]; int pre[maxn]; int pos = 0; void dfs(int u, char *s) { if(u == -1) return; dfs(pre[u],s); s[pos++] = u+'0'; } bool solve(int v, int u) { char s1[maxn],s2[maxn]; //寻找先前的路径 pos = 0; dfs(v,s1); s1[pos] = '\0'; //寻找由u更新的路径 pos = 0; dfs(u,s2); s2[pos++] = v+'0'; s2[pos] = '\0'; if(strcmp(s1,s2) > 0) return true; return false; } int spfa(int s, int t) { queue <int> que; memset(dis,INF,sizeof(dis)); memset(inque,0,sizeof(inque)); memset(pre,-1,sizeof(pre)); dis[s] = 0; inque[s] = 1; que.push(s); while(!que.empty()) { int u = que.front(); que.pop(); inque[u] = 0; for(int v = 1; v <= n; v++) { if(Map[u][v] > 0) { int tmp = dis[u] + Map[u][v] + tax[v]; if(tmp < dis[v])//直接更新 { dis[v] = tmp; pre[v] = u; if(!inque[v]) { inque[v] = 1; que.push(v); } } else if(tmp == dis[v] && solve(v,u)) { pre[v] = u; } } } } return dis[t]; } void output(int s, int t) { int res[maxn]; int cnt = 0; int tmp = t; while(pre[tmp] != -1) { res[cnt++] = tmp; tmp = pre[tmp]; } res[cnt] = s; printf("Path: "); for(int i = cnt; i >= 1; i--) printf("%d-->",res[i]); printf("%d\n",res[0]); } int main() { while(~scanf("%d",&n) && n) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) scanf("%d",&Map[i][j]); } for(int i = 1; i <= n; i++) scanf("%d",&tax[i]); int u,v; while(~scanf("%d %d",&u,&v)) { if(u == -1 && v == -1) break; int tmp1 = tax[u];//备份 int tmp2 = tax[v]; tax[u] = 0; tax[v] = 0; printf("From %d to %d :\n",u,v); int ans = spfa(u,v); output(u,v); printf("Total cost : %d\n\n",ans); //恢复备份 tax[u] = tmp1; tax[v] = tmp2; } } return 0; }
#include <stdio.h> #include <algorithm> #include <set> #include <map> #include <vector> #include <math.h> #include <string.h> #include <stack> #include <queue> #define LL long long #define _LL __int64 using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 110; int Map[maxn][maxn]; int tax[maxn]; int pre[maxn][maxn]; int n; void floyd() { //初始化 for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) pre[i][j] = j; for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(Map[i][k] > 0 && Map[k][j] > 0) { int tmp = Map[i][k] + Map[k][j] + tax[k]; if(tmp < Map[i][j]) //小于直接更新 { Map[i][j] = tmp; pre[i][j] = pre[i][k]; } else if(tmp == Map[i][j]) { pre[i][j] = min(pre[i][k],pre[i][j]); } } } } } } void output(int s, int t) { printf("Path: "); int tmp = s; while(tmp != t) { printf("%d-->",tmp); tmp = pre[tmp][t]; } printf("%d\n",t); } int main() { while(~scanf("%d",&n) && n) { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d",&Map[i][j]); if(Map[i][j] == -1) Map[i][j] = INF; } for(int i = 1; i <= n; i++) scanf("%d",&tax[i]); int u,v; floyd(); while(~scanf("%d %d",&u,&v)) { if(u == -1 && v == -1) break; printf("From %d to %d :\n",u,v); output(u,v); printf("Total cost : %d\n\n",Map[u][v]); } } return 0; }