图算法专题:带/不带优化的Dijkstra、DFS

一、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无限制,全通用时间上较慢 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值