题目链接: POJ 2263 Heavy Cargo
【题目大意】
某公司生产了一种巨型卡车,它的载重量并不在于它本身的载重量, 而只在于公路的载重量!
题目给你 开车运送的起点和终点 , 以及一些可供选择的道路, 每个道路标明了载重量。
让你求出从起点开到终点,卡车的最大载重量是多少 , 也就是起点到终点的通路中,载重量最小的道路的载重量。
【思路】
小技巧: 用map 实现字符串与数字的对应,注意要从1开始。
Dijkstra算法的变形 。
之前学图论的时候这个算法学的太快 没怎么练过,,以为用SPFA完全可以替代,,然而这两种算法的思想并不一样。
Dijkstra算法是从起点开始 ,每次在它相邻的节点中选出距离起点最近的那个点,更新时,将刚选出的节点临近的节点“显化”,也就是将minDist【】的值从INF 更新为当前到起点的距离, 缺点是要遍历n个节点 来找临近的点; 效率 O(n^2)
SPFA算法仅仅把比当前距离短的节点推入队列,再不断的从子节点中通过 松弛操作 找出当前的最短路。
本题是最短路的变形,, 每次寻找的是当前节点 附近节点的最大值而不是最短路,因为要让载重量尽可能的大,用Dijkstra的贪心思想很好想到,
int tmpmin = 0; int addnode; for(int i=1;i<=n;i++){ if(!inqueue[i]&&minDist[i]>tmpmin){ 找出临近节点最大的值 tmpmin = minDist[i]; addnode = i; } }
在节点更新的时候 minDist【】 并不是储存当前最短距离了,而是当前最小的载重量。
所以更新的时候 用临近节点的载重量 与 根节点(刚添加的节点)的载重量 比较选出最小的。
for(int i=0;i<G[addnode].size();i++){ int vex = G[addnode][i].v; int value = G[addnode][i].len; if(!inqueue[vex]&&minDist[vex]<min(value,minDist[addnode])){ minDist[vex]=min(value,minDist[addnode]); } }
【Dijkstra】
#include <iostream> #include <cstdio> #include <map> #include <cstring> #include <vector> using namespace std; const int maxn = 220; struct node{ int v,len; node(int v=0,int len=0):v(v),len(len){} }; int minDist[maxn]; bool inqueue[maxn]; vector<node>G[maxn]; int n,m; void dijkstra(int st,int end){ memset(minDist,0,sizeof(minDist)); memset(inqueue,0,sizeof(inqueue)); minDist[st]=0; inqueue[st]=true; for(int i=0;i<G[st].size();i++){ int vex = G[st][i].v; minDist[vex]=G[st][i].len; } for(int nodenum=1;nodenum<n;nodenum++){ int tmpmin = 0; int addnode; for(int i=1;i<=n;i++){ if(!inqueue[i]&&minDist[i]>tmpmin){ tmpmin = minDist[i]; addnode = i; } } inqueue[addnode]=true; for(int i=0;i<G[addnode].size();i++){ int vex = G[addnode][i].v; int value = G[addnode][i].len; if(!inqueue[vex]&&minDist[vex]<min(value,minDist[addnode])){ minDist[vex]=min(value,minDist[addnode]); } } } } int main(){ map<string,int>mp; // int cas=0; while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){ printf("Scenario #%d\n",++cas); string a,b; mp.clear(); for(int i=0;i<maxn;i++) G[i].clear(); int len; int cnt=0; for(int i=0;i<m;i++){ cin>>a>>b>>len; if(!mp[a]){ //用map 做string 与 int对应的时候,不要从0 开始 ,不好判断 mp[a]=++cnt; //从零开始 这个判断就错了 } if(!mp[b]) mp[b]=++cnt; G[mp[a]].push_back(node(mp[b],len)); G[mp[b]].push_back(node(mp[a],len)); } cin>>a>>b; dijkstra(mp[a],mp[b]); cout<<minDist[mp[b]]<<" tons"<<endl<<endl; } return 0; }
搞懂了上述传递状态的方法,SPFA的代码就很容易写出来了
【SPFA】
#include <iostream> #include <cstdio> #include <map> #include <cstring> #include <queue> #include <vector> #define INF 0xfffffff using namespace std; const int maxn = 220; struct node{ int v,len; node(int v=0,int len=0):v(v),len(len){} }; int minDist[maxn]; bool inqueue[maxn]; vector<node>G[maxn]; int n,m; void SPFA(int st,int end){ memset(minDist,0,sizeof(minDist)); memset(inqueue,0,sizeof(inqueue)); inqueue[st]=true; queue<int >Q; minDist[st]=INF; Q.push(st); while(!Q.empty()){ int vex = Q.front(); Q.pop(); inqueue[vex]=0; for(int i=0;i<G[vex].size();i++){ int v = G[vex][i].v; int value = G[vex][i].len; int tmp = max(minDist[v],min(minDist[vex],value)); if(tmp>minDist[v]){ minDist[v]=tmp; if(!inqueue[v]) { inqueue[v]=1; Q.push(v); } } } } } int main(){ map<string,int>mp; // int cas=0; while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){ printf("Scenario #%d\n",++cas); string a,b; mp.clear(); for(int i=0;i<maxn;i++) G[i].clear(); int len; int cnt=0; for(int i=0;i<m;i++){ cin>>a>>b>>len; if(!mp[a]){ //用map 做string 与 int对应的时候,不要从0 开始 ,不好判断 mp[a]=++cnt; //从零开始 这个判断就错了 } if(!mp[b]) mp[b]=++cnt; G[mp[a]].push_back(node(mp[b],len)); G[mp[b]].push_back(node(mp[a],len)); } cin>>a>>b; SPFA(mp[a],mp[b]); cout<<minDist[mp[b]]<<" tons"<<endl<<endl; } return 0; }
这一题在比赛的时候没有想出来怎么写,还是对算法原理掌握的不够清晰,光看模板是敲不出来新题的!