1131. Subway Map (30)[dfs剪枝+回溯]

1. 原题: https://www.patest.cn/contests/pat-a-practise/1131

2. 思路:

题意:给出一个图,求起点到终点的最短路径。单源最短路径问题。
思路:
虽然求最短路,不过有要求,同时是最小换乘。
这样该怎么好呢。
求最短路,无外乎dfs和dijkstra。显然,对于该题,
我们不需要遍历某点到所有点的最短路。那就用dfs算法了。
为了判断换乘的问题,我们单独把两点的所属线路存储到数组stops里。
在dfs的时候进行判断即可。
dfs时,同时要进行剪枝优化和回溯。
输出结果就简单了。
已AC.

3. 源码:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int MaxN = 10000;
int stops[MaxN][MaxN] = { 0 };//存储两点之间的所属线路
vector<int> vs[MaxN];//邻接表的二维数组表示
int visited[MaxN] = { 0 };//某点是否已经访问
int minDep, minTrans, s, d;//分别为最小站点数,最小换乘,起点和终点
vector<int> path, curPath;//分别为存储结果的路径及dfs的临时路径

void dfs(int start, int depth, int trans, int lastLine);//dfs递归

int main()
{
	//freopen("in.txt", "r", stdin);
	int N;
	scanf("%d", &N);
	for (int i = 1; i <= N; i++)//读入数据
	{
		int M, pre;//pre存储上一个结点
		scanf("%d %d", &M, &pre);
		for (int j = 1; j < M; j++)
		{
			int node;
			scanf("%d", &node);
			vs[node].push_back(pre);
			vs[pre].push_back(node);
			stops[pre][node] = stops[node][pre] = i;
			pre = node;
		}
	}

	int K;
	scanf("%d", &K);
	for (int i = 0; i < K; i++)
	{
		scanf("%d %d", &s, &d);

		minDep = minTrans = 0x7fffffff;//初始为最大
		path.clear();
		curPath.clear();
		memset(visited, 0, sizeof(visited));
		dfs(s, 0, 0, 0);

		printf("%d\n", minDep);
		int lastLine = stops[path[0]][path[1]];//上一条线路
		int lastStop = path[0];//上一个乘车点

		int j = 1;
		while (1)
		{
			while (j < path.size() && stops[path[j - 1]][path[j]] == lastLine) j++;//同一线路,一直遍历
			printf("Take Line#%d from %04d to %04d.\n", lastLine, lastStop, path[j - 1]);
			if (j == path.size())//遍历结束
				break;
			lastStop = path[j - 1];//更新上一个乘车点
			lastLine = stops[path[j - 1]][path[j]];//更新上一条线路
		}
	}

	return 0;
}


void dfs(int start, int depth, int trans, int lastLine)//dfs递归
{
	if (depth > minDep)//剪枝优化,下面的不需要继续递归遍历了
		return;

	visited[start] = 1;//访问过该点了,置1
	curPath.push_back(start);//压入临时路径

	if (start == d)//到达终点
	{
		if (depth < minDep || (depth == minDep && trans < minTrans))
		{
			minDep = depth;
			minTrans = trans;
			path.clear();
			path = curPath;
		}
	}

	for (auto it = vs[start].begin(); it != vs[start].end(); it++)//遍历下面的所有点
	{
		if (visited[*it] == 0)
		{
			if (stops[start][*it] != lastLine)//此处,换乘数可以加1了
				dfs(*it, depth + 1, trans + 1, stops[start][*it]);
			else
				dfs(*it, depth + 1, trans, lastLine);
		}
	}

	visited[curPath.back()] = 0;//到这里是回溯回来,恢复为未访问
	curPath.pop_back();//同时弹出该点
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值