PAT_1030 Travel Plan (30 分) (dij+dfs)

题目链接
在这里插入图片描述
在这里插入图片描述

这道题就当作dij+dfs练习用的,需要特别留心的就是各个数组、临时变量的初始化问题,不要总是debug的时候才发现是某个地方没有及时初始化导致出错,比如dst数组先初始化为INF,再将dis[src]设置为0,开始dij算法,dfs的时候,记录第二尺度最优的变量也要记得初始化。

代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define maxNode 505
#define INF 0x3f3f3f3f
int G[maxNode][maxNode];
int costs[maxNode][maxNode]={0};
bool vis[maxNode]={false};
int dis[maxNode];
vector<int> pre[maxNode];//无需初始化

int optValue=INF;
vector<int> path,tmpPath;//存储最短路径
void dij(int src,int N){
    for(int i=0;i<N;i++)
        dis[i]=INF;
    dis[src]=0;
    for(int i=0;i<N;i++){//跑N次dij
        int v=-1,tmpMin=INF;
        for(int w=0;w<N;w++){//从未收录顶点中找dis最小的进行收录
            if(!vis[w] && dis[w]<tmpMin){
                v=w;
                tmpMin=dis[w];
            }
        }
        if(v==-1) return;//说明图不连通
        vis[v]=true;//将v收录,并更新邻接点的dst
        for(int w=0;w<N;w++){
            //cout << G[v][w] << " " << dis[v]+G[v][w] << " " << dis[w] << endl;
            if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]<dis[w]){
                dis[w]=dis[v]+G[v][w];
                pre[w].clear();
                pre[w].push_back(v);
            }else if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]==dis[w]){
                pre[w].push_back(v);
            }
        }
    }
}
void dfs(int v,int src){
    //递归边界
    if(v==src){
        tmpPath.push_back(v);//此时起点要记得push进去
        //计算第二尺度
        int cost=0;
        for(int i=tmpPath.size()-1;i>0;i--){
            cost+=costs[tmpPath[i]][tmpPath[i-1]];
        }
        if(cost<optValue){
            optValue=cost;
            path=tmpPath;
        }
        tmpPath.pop_back();
        return;
    }
    //递归式
    tmpPath.push_back(v);
    for(int i=0;i<pre[v].size();i++){
        dfs(pre[v][i],src);
    }
    tmpPath.pop_back();
}
int main() {
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt","r",stdin);
#endif
    int N,M,src,dst;
    cin >> N >> M >> src >> dst;
    //初始化图
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            if(i==j) G[i][j]=0;
            else G[i][j]=INF;
        }
    }
    //建图
    for(int i=0;i<M;i++){
        int v,w,dis,cost;
        cin >> v >> w >> dis >> cost;
        G[v][w]=dis;
        G[w][v]=dis;
        costs[v][w]=cost;
        costs[w][v]=cost;
    }
    //跑dij
    dij(src,N);
    //然后进行dfs选择第二尺度最优的
    tmpPath.push_back(dst);//先把终点push进去
    for(int i=0;i<pre[dst].size();i++){
        dfs(pre[dst][i],src);//递归边界是到访问到起点
    }
    for(int i=path.size()-1;i>=0;i--){
        cout << path[i] << " ";
    }
    cout << dis[dst] << " " << optValue;
    return 0;
}

·
·
·
·
·
·

另一种回溯方式是在for循环中先push进去v,再dfs(v),再pop出来v,这样在递归边界就不用专门把最后一个顶点在push pop的操作了。
代码如下:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define maxNode 505
#define INF 0x3f3f3f3f
int G[maxNode][maxNode];
int costs[maxNode][maxNode]={0};
bool vis[maxNode]={false};
int dis[maxNode];
vector<int> pre[maxNode];//无需初始化

int optValue=INF;
vector<int> path,tmpPath;//存储最短路径
void dij(int src,int N){
    for(int i=0;i<N;i++)
        dis[i]=INF;
    dis[src]=0;
    for(int i=0;i<N;i++){//跑N次dij
        int v=-1,tmpMin=INF;
        for(int w=0;w<N;w++){//从未收录顶点中找dis最小的进行收录
            if(!vis[w] && dis[w]<tmpMin){
                v=w;
                tmpMin=dis[w];
            }
        }
        if(v==-1) return;//说明图不连通
        vis[v]=true;//将v收录,并更新邻接点的dst
        for(int w=0;w<N;w++){
            //cout << G[v][w] << " " << dis[v]+G[v][w] << " " << dis[w] << endl;
            if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]<dis[w]){
                dis[w]=dis[v]+G[v][w];
                pre[w].clear();
                pre[w].push_back(v);
            }else if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]==dis[w]){
                pre[w].push_back(v);
            }
        }
    }
}
void dfs(int v,int src){
    //递归边界
    if(v==src){
        //tmpPath.push_back(v);
        //计算第二尺度
        int cost=0;
        for(int i=tmpPath.size()-1;i>0;i--){
            cost+=costs[tmpPath[i]][tmpPath[i-1]];
        }
        if(cost<optValue){
            optValue=cost;
            path=tmpPath;
        }
        //tmpPath.pop_back();
        return;
    }
    //递归式
    //tmpPath.push_back(v);
    for(int i=0;i<pre[v].size();i++){
        tmpPath.push_back(pre[v][i]);
        dfs(pre[v][i],src);
        tmpPath.pop_back();
    }
    //tmpPath.pop_back();
}
int main() {
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt","r",stdin);
#endif
    int N,M,src,dst;
    cin >> N >> M >> src >> dst;
    //初始化图
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            if(i==j) G[i][j]=0;
            else G[i][j]=INF;
        }
    }
    //建图
    for(int i=0;i<M;i++){
        int v,w,dis,cost;
        cin >> v >> w >> dis >> cost;
        G[v][w]=dis;
        G[w][v]=dis;
        costs[v][w]=cost;
        costs[w][v]=cost;
    }
    //跑dij
    dij(src,N);
    //然后进行dfs选择第二尺度最优的
    tmpPath.push_back(dst);//先把终点push进去
    //也可以放在循环里面,先push再dfs再pop出来回溯
    for(int i=0;i<pre[dst].size();i++){
        tmpPath.push_back(pre[dst][i]);
        dfs(pre[dst][i],src);//递归边界是到访问到起点
        tmpPath.pop_back();
    }
    for(int i=path.size()-1;i>=0;i--){
        cout << path[i] << " ";
    }
    cout << dis[dst] << " " << optValue;
    return 0;
}

其实下面这一段(88到97行)也是可以简化的,因为下面这个for循环(93-97行)在dfs中也有:

	//跑dij
    dij(src,N);
    //然后进行dfs选择第二尺度最优的
	tmpPath.push_back(dst);//先把终点push进去
    //也可以放在循环里面,先push再dfs再pop出来回溯
    for(int i=0;i<pre[dst].size();i++){
        tmpPath.push_back(pre[dst][i]);
        dfs(pre[dst][i],src);//递归边界是到访问到起点
        tmpPath.pop_back();
    }

因此可以直接写成如下方式:

	//跑dij
    dij(src,N);
    //然后进行dfs选择第二尺度最优的
    tmpPath.push_back(dst);//先把终点push进去
    dfs(dst,src);

感觉后面这种写法更好,就尽量统一起来,后者好在如果src和dst相同,也能进入到dfs中,在递归边界中把存储最优路径的path更新掉,如果是上一种方式,pre[dst].size()将为0,for循环不会执行,也就没有进入递归,path不会更新,不过题目应该一般不会有起点和终点一样的情况。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值