一、Dijkstra+堆优化pq
例题:PAT A1072 Gas Station (30point(s))
AC代码
#include <iostream>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;
#define ms(Arr,Val) memset(Arr,Val,sizeof(Arr))
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
//20200902 06:45:25 ~-=0.5month
//AC 22ms G++6.5.0
//Dijkstra:求所有单S到其余所有点的最短路MinD,并按一定的筛选和判优条件输出
/*本代码使用了带堆优化(即pq)的Djikstra,时间复杂度O(VlogV+E)
*注意点:
*1) Case4用纯dfs会T;
*2) Case0/2卡了判优条件(之前我的Cmp函数那块笔误了,以后少笔误!!!);
*3) 还有就是G点和普通点建议连号,不然处理起来很不方便,容易WA.(1~N1+N2)
*/
struct Station{
int id;
double minD,avgD;
};
bool Cmp(const Station& s1,const Station& s2){
if(s1.minD!=s2.minD) return s1.minD>s2.minD;
else if(s1.avgD!=s2.avgD) return s1.avgD<s2.avgD;
else return s1.id<s2.id;
}
const int MAXN = 1e3+10;
const int MAXG = 1e1;
int MinD[MAXN+5];
bool visited[MAXN+5];
vector<map<int,int>> G(MAXN+5);
vector<Station> stats(MAXG+5);
int N1,N2,R,maxRg,N;
bool flag;
int Idx(char c[]){
int id;
if(c[0]!='G'){
sscanf(c,"%d",&id);
return id;
}
else{
sscanf(c+1,"%d",&id);
return id+N1;
}
}
void dijkstra(int S){
ms(MinD,INF); ms(visited,false);
MinD[S] = 0;
priority_queue<P,vector<P>,greater<P>> q;
q.push(P(MinD[S],S));
while(!q.empty()){
int x = q.top().second,m = q.top().first;
q.pop();
if(m>MinD[x]) continue;
if(MinD[x]==INF) return;
visited[x] = true;
int d,l;
for(auto it=G[x].begin();it!=G[x].end();++it){
int d = it->first,l = it->second;
if(!visited[d] && MinD[d]>MinD[x]+l){
MinD[d] = MinD[x]+l;
q.push(P(MinD[d],d));
}
}
}
}
int main(){
// freopen("input.txt","r",stdin);
// freopen("myans.txt","w",stdout);
char p1[5],p2[5];
int ni,nj,l,minD,maxD;
double sumD;
while(~scanf("%d %d %d %d",&N1,&N2,&R,&maxRg)){
G.clear(); G.resize(MAXN+5);
stats.clear();
N = N1+N2;
for(int i=1;i<=R;++i){
scanf("%s %s %d",p1,p2,&l);
ni = Idx(p1); nj = Idx(p2);
G[ni][nj] = G[nj][ni] = l;
}
for(int i=1;i<=N2;++i){
dijkstra(i+N1);
minD = INF; maxD = -1; sumD = 0;
flag = true;
for(int j=1;j<=N1;++j){
if(MinD[j]>maxRg){
flag = false;
break;
}
sumD += MinD[j];
maxD = max(maxD,MinD[j]);
minD = min(minD,MinD[j]);
}
if(flag) stats.push_back(Station{i,minD,sumD/N1});
}
sort(stats.begin(),stats.end(),Cmp);
if(!stats.size()) printf("No Solution\n");
else printf("G%d\n%.1lf %.1lf\n",stats[0].id,stats[0].minD,int(stats[0].avgD*10+0.5)/10.0);
}
return 0;
}
二、Dijkstra普通版+DFS
例题:PAT A1111 Online Map (30point(s))
AC代码
#include <iostream>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;
#define ms(Arr,Val) memset(Arr,Val,sizeof(Arr))
#define INF 0x3f3f3f3f
//8:18~8:25~16:47 ~0.5day
//AC 203ms G++6.5.0
//Dijkstra+DFS: 必须用此法,直接用dfs不论如何剪枝Case4都会TLE...
/*原先v1代码用纯dfs做的,结果Case4一直T,于是参考柳神和晴神(算法笔记书)的代码,写出了v2,然后就AC了。
*/
struct Road{
int l,t;
};
const int MAXN = 5e2;
const int MAXC = 1e4;
bool visited[MAXN+5];
vector<vector<int>> G(MAXN+5); //原图
vector<vector<int>> pG(MAXN+5); //Dijkstra简化后的逆序有向图,图中只包含从S到其它联通点的反向一级最短路
map<int,map<int,Road>> Map;
int MinD[MAXN+5]; //nodeId->minLen
int MinT[MAXN+5]; //nodeId->minTime
vector<int> path,sPath,fPath;
int N,R,S,D,minD,minT;
int minTime,minSteps;
void dijkstra1(){ //正向dijkstra: 将G简化为只含从S到其它联通点的反向一级最短路的有向图pG。
for(int i=0;i<N;++i){
int x = -1, MIN = INF; //寻找V-S中MinD最小的节点x
for(int j=0;j<N;++j){
if(!visited[j] && MinD[j]<MIN){
x = j;
MIN = MinD[j];
}
}
if(x==-1) return; //找不到直接RET
visited[x] = true; //visited置位 (即x并入S)
for(int j=0;j<G[x].size();++j){ //以x为中转对V-S更新MinD和pG
int d = G[x][j];
if(!visited[d]){
int curD = MinD[x]+Map[x][d].l;
if(curD<MinD[d]){
MinD[d] = curD;
pG[d].clear();
pG[d].push_back(x);
}
else if(curD==MinD[d]){
pG[d].push_back(x);
}
}
}
}
}
void dfs1(int x,int totalTime){ //逆向dfs:在pG上从D出发遍历查找到S的2~n级最短路。
if(x==S){ //(注):1)形参表长为n,包括1个当前待拓展的节点Id,及其余n-1级判优信息;
if(totalTime<minTime){ // 2)不需要用visited,因为pG里都是各点的最短路,不可能有回头路.
minTime = totalTime;// 3)其他写法和原来的纯dfs一样,比如仍可以加入一些剪枝操作.
sPath = path;
}
return;
}
for(int i=0;i<pG[x].size();++i){
int s = pG[x][i];
if(totalTime+Map[s][x].t>minTime) continue;
path.push_back(s);
dfs1(s,totalTime+Map[s][x].t);
path.pop_back();
}
}
void dijkstra2(){ //正向
for(int i=0;i<N;++i){
int x = -1, MIN = INF;
for(int j=0;j<N;++j){
if(!visited[j] && MinT[j]<MIN){
x = j;
MIN = MinT[j];
}
}
if(x==-1) return;
visited[x] = true;
for(int j=0;j<G[x].size();++j){
int d = G[x][j];
if(!visited[d]){
int curT = MinT[x]+Map[x][d].t;
if(curT<MinT[d]){
MinT[d] = curT;
pG[d].clear();
pG[d].push_back(x);
}
else if(curT==MinT[d]){
pG[d].push_back(x);
}
}
}
}
}
void dfs2(int x,int steps){ //逆向
if(x==S){
if(steps<minSteps){
minSteps = steps;
fPath = path;
}
return;
}
for(int i=0;i<pG[x].size();++i){
int s = pG[x][i];
if(steps+1>minSteps) continue;
path.push_back(s);
dfs2(s,steps+1);
path.pop_back();
}
}
bool isIdent(){
if(sPath.size()!=fPath.size()) return false;
for(int i=0;i<sPath.size();++i){
if(sPath[i]!=fPath[i]) return false;
}
return true;
}
int main(){
// freopen("input.txt","r",stdin);
// freopen("myans.txt","w",stdout);
int ni,nj,oneWay,l,t;
while(~scanf("%d %d",&N,&R)){
G.clear(); G.resize(MAXN+5);
Map.clear();
for(int i=0;i<R;++i){
scanf("%d %d %d %d %d",&ni,&nj,&oneWay,&l,&t);
G[ni].push_back(nj);
Map[ni][nj] = Road{l,t};
if(oneWay==0){
G[nj].push_back(ni);
Map[nj][ni] = Road{l,t};
}
}
scanf("%d %d",&S,&D);
for(int i=1;i<=2;++i){
ms(visited,false); ms(MinD,INF); ms(MinT,INF);
pG.clear(); pG.resize(MAXN+5);
path.clear();
MinD[S] = MinT[S] = 0;
minTime = minSteps = INF;
if(i==1){
dijkstra1();
dfs1(D,0);
minD = MinD[D];
}
else{
dijkstra2();
dfs2(D,0);
minT = MinT[D];
}
}
if(!isIdent()){
printf("Distance = %d: ",minD);
for(int i=sPath.size()-1;i>=0;--i){
printf("%d -> ",sPath[i]);
}
printf("%d\nTime = %d: ",D,minT);
for(int i=fPath.size()-1;i>=0;--i){
printf("%d -> ",fPath[i]);
}
}
else{
printf("Distance = %d; Time = %d: ",minD,minT);
for(int i=sPath.size()-1;i>=0;--i){
printf("%d -> ",sPath[i]);
}
}
printf("%d\n",D);
}
return 0;
}
三、纯DFS
例题:PAT A1030 Travel Plan (30point(s))
AC代码
#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ms(Arr,Val) memset(Arr,Val,sizeof(Arr))
#define INF 1<<30
//10:38~11:01 23min
//AC 5ms G++6.5.0
//dfs裸题,两级判优: ZOJ上该题测试数据太基础,不加任何剪枝优化也能AC。。。对算法的时间性能要求太低了
/*与Bailian1724 Roads的对比:
*POJ那题是要求在totalCost<=K的前提下找到totalLen最短的路线,比本题情况要复杂,需要在minLen上进一步剪枝,开单个数字记录minL无法回溯,
*故使用minL[r.d][totalCost]表示费用为totalCost,且当前节点是r.d时的minLen值。
*/
struct Road{
int d,l,t; //dest,distance,cost
};
const int MAXN = 5e2;
bool visited[MAXN+5];
vector<vector<Road>> G(MAXN+5);
int path[MAXN+5];
int bestPath[MAXN+5];
int N,R,S,D;
int minLen,minCost,minSteps;
void dfs(int x,int totalLen,int totalCost,int steps){
if(x==D){
if(totalLen<minLen || totalLen==minLen && totalCost<minCost){
minLen = totalLen;
minCost = totalCost;
minSteps = steps;
for(int i=0;i<=minSteps;++i) bestPath[i] = path[i];
}
return;
}
Road r;
for(int i=0;i<G[x].size();++i){
r = G[x][i];
if(!visited[r.d]){
if(totalLen+r.l>minLen) continue;
if(totalLen==minLen && totalCost+r.t>minCost) continue;
visited[r.d] = true;
path[steps+1] = r.d;
dfs(r.d,totalLen+r.l,totalCost+r.t,steps+1);
visited[r.d] = false;
}
}
}
int main(){
// freopen("input.txt","r",stdin);
// freopen("myans.txt","w",stdout);
int ni,nj,l,t;
while(cin>>N>>R>>S>>D){
for(int i=0;i<G.size();++i) G[i].clear();
ms(visited,false); ms(path,0); ms(bestPath,0);
for(int i=0;i<R;++i){
cin>>ni>>nj>>l>>t;
G[ni].push_back(Road{nj,l,t});
G[nj].push_back(Road{ni,l,t});
}
minLen = minCost = minSteps = INF;
visited[S] = true;
path[0] = S;
dfs(S,0,0,0);
for(int i=0;i<=minSteps;++i){
cout<<bestPath[i]<<" ";
}
cout<<minLen<<" "<<minCost<<endl;
}
return 0;
}
四、总结
算法 | 适用场景 | 特点 | 备注 |
Dijkstra(pq)+DFS | 无负权图,要求路径或多级判优 | 速度快,复杂度O(VlogV+E) | 推荐考试时写这个(节约时间) |
Dijkstra(pq) | 无负权图,不输出路径 | 速度快,复杂度O(VlogV+E) | |
纯DFS | 无限制,全通用 | 时间上较慢 |