最短路的简单应用问题。
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3790
CSUST链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=19760#problem/L
3 2 1 2 5 6 2 3 4 5 1 3 0 0
9 11
解题思路:Dijkstra
求最短路径直接用Dijkstra算法模板即可,由于本题多了个费用问题,所以在松弛dist[y] 时把原来的直接找出最短路径替换变成分情况讨论即可。
1、如果新路径(即沿着代码中 x 出发的边走)路径变短,则任然直接替换,同时更新花费。
2、如果路径长度相等,则选花费小的,只用更新花费即可。
PS: 1、注意是无向图,不要忽略了重边问题。
2、如果你不知道Dijkstra 请先AC这道题 POJ 1847 Tram
http://blog.csdn.net/cfreezhan/article/details/8619040
3、另外推荐 POJ 1062 昂贵的聘礼
http://blog.csdn.net/cfreezhan/article/details/8627216
//8184 KB 187 ms C++ 1534 B 2013-03-01 18:40:56 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1000 + 10; const int INF = 100000000; int w[maxn][maxn]; int value[maxn][maxn]; int dist[maxn],cost[maxn]; bool vis[maxn]; int n,m; int start,end; void Dijkstra() { memset(vis,false,sizeof(vis));//清除所有标点 for(int i=1;i<=n;i++) { dist[i] = w[start][i]; cost[i] = value[start][i]; } dist[start] = 0; //起点自己到自己为0 cost[start] = 0; vis[start] = true; for(int i=1;i<=n;i++) //循环n次 { int x, m = INF; for(int y=1;y<=n;y++) //找出未标记的 dist 的最小的节点x { if(!vis[y] && dist[y] <= m) { m = dist[x=y]; } } vis[x] = true; //标记 x for(int y = 1;y <= n;y++) //松弛操作,更新dist { if(dist[y] > dist[x]+w[x][y]) //如果比dist小可直接更新 { dist[y] = dist[x] + w[x][y]; cost[y] = cost[x] + value[x][y]; } else if(dist[y] == dist[x]+w[x][y]) //如果路径等长, 则选择花费小的 { if(cost[y] > cost[x] + value[x][y]) { cost[y] = cost[x] + value[x][y]; dist[y] = dist[x] + w[x][y];//其实本来就相等 可以省略 } } //dist[y] = min(dist[y], dist[x]+w[x][y]); } } printf("%d %d\n",dist[end],cost[end]); } int main() { int a, b, d, p; while(scanf("%d%d", &n, &m)!=EOF) { if(n==0 || m==0) break; for(int i=1;i<=n;i++) //初始化 { dist[i] = INF; cost[i] = INF; for(int j=1;j<=n;j++) { w[i][j] = INF; value[i][j] = INF; } } for(int i=1;i<=m;i++) { scanf("%d%d%d%d", &a, &b, &d, &p); if(d <= w[a][b]) //注意重边问题 { w[a][b] = d; w[b][a] = d; //注意是无向图 value[a][b] = p; value[b][a] = p; } } scanf("%d%d", &start, &end); Dijkstra(); } return 0; }
最近开始学图论了,难得的1A简单题目,看来还是要多学算法,从模板题目开始慢慢做才会有进步呢。
8-12日,重做了一遍,居然没有发现是做过的还是 1A的,然后果断没有考虑重边问题 WA了,然后想起有重边,输入时处理还是没弄好又WA了一次Orz
贴个一样的代码。。。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn = 1000+10; const int INF = maxn*100000; int w[maxn][maxn]; int p[maxn][maxn]; int d[maxn]; int dp[maxn]; int vis[maxn]; int n,m; int s,t; void Dijkstra() { for(int i = 1; i <= n; i++) d[i] = w[s][i]; for(int i = 1; i <= n; i++) dp[i] = p[s][i]; d[s] = 0; dp[s] = 0; memset(vis, 0, sizeof(vis)); for(int i = 0; i < n; i++) { int x, m = INF; for(int y = 1; y <= n; y++) if(!vis[y] && d[y] <= m) m = d[x=y]; vis[x] = 1; for(int y = 1; y <= n; y++) { if(d[x]+w[x][y] < d[y]) { d[y] = d[x]+w[x][y]; dp[y] = dp[x]+p[x][y]; } else if(d[x]+w[x][y] == d[y]) { dp[y] = min(dp[y],dp[x]+p[x][y]); } } } printf("%d %d\n", d[t], dp[t]); } int main() { while(scanf("%d%d", &n,&m) != EOF) { if(n == 0 && m == 0) break; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { w[i][j] = (i == j ? 0 : INF); p[i][j] = (i == j ? 0 : INF); } } int u,v, dist, pay; for(int i = 0; i < m; i++) { scanf("%d%d%d%d",&u,&v,&dist,&pay); if(dist < w[u][v]) { w[u][v] = w[v][u] = dist; p[v][u] = p[u][v] = pay; } } scanf("%d%d", &s,&t); Dijkstra(); } return 0; }