最短路径与搜索之打印路径

--打印路径

本篇将汇总floyd, spfa, bfs , dfs 以及01背包的路径打印思想及代码实现, 是较为冷门的acm题目考点, 但其中的思想要求每个程序员掌握(咳咳...虽然我也刚刚学会...)

Floyd算法:

floyd算法是最基础的最短路径算法,因此优先讲解(此篇将默认各位读者已了解相应算法原理, 故只解释打印路径的代码)

#include<iostream>
#include<stack>
using namespace std;
inta[100][100];
intpath[100][100];

int main(void) {

 int n, m;   //其中n为点的个数, m为待输入路径数
 
 while(cin >> n >> m) {

        
memset(path, 0, sizeof(path));        //初始化路径数组
        memset(a, 0x3f, sizeof(a));  
 
        for(int i = 1; i <= m; i++) {
            int sta, ed, len;
            cin >> sta >> ed >> len;
 
            if(len < a[sta][ed]) {   //相同两点的路径取最小的
                a[sta][ed] = len;
                a[ed][sta] = len;
            }
        }
 
        for(int k = 1; k <= n; k++) {
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++) {
                    if(a[i][j] >a[i][k] +a[k][j]) {
                        a[i][j] =a[i][k] +a[k][j];        
                        path[i][j] = k;    //path[i][j]记录i 到 j 的间隔点,如果无间隔点,则值为0.
                    
                }
            }
        }
 
        stack<int>s;
        int sta = 1, ed = n;   //假设要求输出点1 到 点n 的路径, 则从n点开始向前寻找间隔点
       直到无间隔点为止, 这时将起点和与起点相邻的点放入栈中
        while(path[sta][ed] != 0) {
            s.push(ed);
            ed =path[sta][ed];
        }
        s.push(ed);
        s.push(sta);
        while(!s.empty()) {   //输出路径
            cout << s.top() << endl;
            s.pop();
        }
        cout << endl;
    }
}


SPFA算法:

spfa是单源最短路径的快速算法, 是迪杰斯特拉算法的优化(个人认为), 平时较为常见:

#include<stack>
#include<queue>

void spfa(int sta);
using namespace std;
int a[1000][1000];
int dist[1000];                            //记录起点到i点的距离
int path[1000];   //记录路径, 存储间隔点, path[i]即为起点与i点的间隔点,无间隔点,则为0
bool vist[1000];   //标记点是否使用过
int n, m;
int main(void) {


 cin >> n >> m;    //假设n为点的个数, m为待输入路径数
 memset(a, 0x3f, sizeof(a));
 for(int i = 1; i <= m; i++) {
  int sta, ed, len;
  cin >> sta >> ed >> len;

  if(len < a[sta][ed]) {    //相同两点的路径取最小的
   a[sta][ed] = len;
   a[ed][sta] = len;
  }
 

 spfa(1);
 stack<int> s;
 while(path[n] != 0) {
  s.push(n);
  n = path[n];
 }
 s.push(1);
 
 while(!s.empty() ){
  cout << s.top() << endl;
  s.pop();
 

}

void spfa(intsta) {

  memset(dist, 0x3f, sizeof(dist));   //dist数组初始化为inf, 表示两点不连通
  memset(vist, 0x3f, sizeof(vist));   //vist素组初始化为未标记

  memset(path, 0, sizeof(path));
  queue<int> q;
  q.push(sta);
  vist[sta] = 1;
  dist[sta] = 0;
  while(!q.empty()) {
   int gap = q.front(); q.pop();

   for(int j = 1; j <= n; j++) {
    if(dist[j] >dist[gap] + a[gap][j]) {
     dist[j] =dist[gap] + a[gap][j];
     path[j] =gap    

     //path数组记录起点与j点的间隔点

     if(!vist[j]) {
      q.push(j);
      vist[j] = 1;
     }
    }
   }
  }
}


BFS搜索:

bfs算法用于解决最优解问题, 此处只是打印最短的路径

#include<iostream>
#include<queue>
#include<stack>

using namespace std;
int n, m;
struct Node {
 int x, y;
} sta;
char a[100][100];
int vist[100][100];
void bfs(Node sta);
int path[100][100];
int main(void) {

 cin >>n >> m;     //n为行数, m为列数
 for(int i = 0; i <n; i++) {
  for(int j = 0; j <m; j++) {
   cin >> a[i][j];

   if(a[i][j] == 'S') {   //记录起始点坐标
   sta.x = i;
   sta.y = j;
   }
  }
 

 bfs(sta);
}


void bfs(Node sta) {

 memset(vist, 0, sizeof(vist));
 vist[sta.x][sta.y] = 1;
 stack<Node> sa;
 queue<Node> q;
 q.push(sta);
 while(!q.empty()) {
  Node va;
  va = q.front(); q.pop();
  if(a[va.x][va.y] == 'E') {

   Node tmp;
   int x = va.x, y = va.y;
   while(!(x == sta.x && y == sta.y)) {    //回溯
    tmp.x = x;
    tmp.y = y;
    sa.push(tmp);
    x =path[tmp.x][tmp.y] / m;
    y =path[tmp.x][tmp.y] % m;

   }
   sa.push(sta);       //起点入栈
   while(!sa.empty()) {     //输出
    tmp = sa.top();
    cout << "(" << tmp.x << " " << tmp.y << ")" << endl;
    sa.pop();
   }
   return; 
  }
  intxx[] = {1, -1, 0, 0};
  int yy[] = {0, 0, 1, -1};
  for(int i = 0; i < 4; i++) {
   Node vb;
   vb.x = va.x + xx[i];
   vb.y = va.y + yy[i];
   if(vb.x >= 0 && vb.x < n&& vb.y >= 0&& vb.y < m&& !vist[vb.x][vb.y]&& a[vb.x][vb.y] != '#') {
    q.push(vb);
    vist[vb.x][vb.y] = 1;
    path[vb.x][vb.y] = va.x * m + va.y;      //记录该点的上一点

注:每个点的二维坐标(x, y)均可一维表示, 即(x, y) <=> x * 列数 + y, 且此值在一个坐标系中是唯一的
   }
  }
 }
}


DFS搜索:

dfs搜索可打印全部连通的路径, 代码实现如下:

#include<cstring>
#include<iostream>
#include<stack>

using namespace std;
char a[100][100];
int vist[100][100];
struct Node {
 int x, y;
} sta;
stack<Node> sa, sb;
int cnt = 0;
void dfs(Node sta);
int n, m;
int main(void) {
 while(cin >> m >> n) {     //m为行数, n为列数
  memset(vist, 0, sizeof(vist));
  for(int i = 0; i < m; i++) {
   for(int j = 0; j < n; j++) {
    cin >> a[i][j];
    if(a[i][j] == 'S') {
     sta.x = i;
     sta.y = j;
    }
   }
  }
  sa.push(sta);      //起点入栈并标记
  vist[sta.x][sta.y] = 1;
  dfs(sta);
 }
}


void dfs(Node sta) {

 if(a[sta.x][sta.y] == 'E') {    //判断是否到达终点
          
  cout << cnt++ << ":" << endl;   //cnt记录是第几条满足条件的路径
  while(!sa.empty()) {     
   Node temp = sa.top();       //到达终点, 由于栈中记录的点是反向的,故借助sb栈调整顺序输出
   sb.push(temp);        
   sa.pop();
  }
  while(!sb.empty()) {     //sa栈还要回溯, 故将sb中的元素还原回去
   Node temp = sb.top();
   cout << "(" << temp.x << " " << temp.y << ")" << endl;
   sa.push(temp);
   sb.pop();
  }
 }

 int xx[] = {0, 0, 1, -1};
 int yy[] = {1, -1, 0, 0};
 Node vb;
 for(int i = 0; i < 4; i++) {
  vb.x =   sta.x + xx[i];
  vb.y = sta.y + yy[i];
  if(sta.x >= 0&& sta.x < m&& sta.y >= 0&& sta.y < n&& !vist[vb.x][vb.y]&& a[vb.x][vb.y] != '#') {
   vist[vb.x][vb.y] = 1;
   sa.push(vb);      //满足条件的点入栈, 加入路径
   dfs(vb);
   vist[vb.x][vb.y] = 0;     //回溯, 使已标记的点变为未标记并出栈
   sa.pop();
  }
 }

 return;


01背包问题:

01背包问题打印路径必须建立在n * n 的空间复杂度上, 优化算法无法记录路径:

#include<iostream>
#include<cstring>
#include<stack>
#include<queue>

using namespace std;
int n, m;
struct Node {
 int mrk, p, v;
} a[1005]; 
int dp[500][1005];
bool path[500][1005];
int main(void) {


 memset(path, 0, sizeof(path));
 while(cin >> n >> m) {
  for(int i = 1; i <= n; i++) {
   cin >> a[i].mrk >> a[i].p >> a[i].v;      //输入标号, 价格, 价值
  }

  for(int i = 1; i <= n; i++) {
   for(int j = 0; j <= m; j++) {

    dp[i][j] =dp[i - 1][j];
    if(j >= a[i].p) {

     if(dp[i][j] <dp[i - 1][j - a[i].p] + a[i].v) {
      dp[i][j] = dp[i - 1][j - a[i].p] + a[i].v;
      path[i][j] = 1;     //记录该资金下是否购买该物品
     }
    }
   }
  }

  for(int i = n; i >= 1 && m > 0; i--) {   //输出物品标号

   if(path[i][m] == true) { 
   cout << a[i].mrk << endl;
   m -= a[i].p;
   }
  }
 
}

总结:路径打印除对dfs 是递归回溯过程外, floyd,spfa, bfs, 01背包问题等均为同一方法,只是实现不尽相同, 掌握一种即可快速上手.


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值