周游世界是件浪漫事,但规划旅行路线就不一定了…… 全世界有成千上万条航线、铁路线、大巴线,令人眼花缭乱。所以旅行社会选择部分运输公司组成联盟,每家公司提供一条线路,然后帮助客户规划由联盟内企业支持的旅行路线。本题就要求你帮旅行社实现一个自动规划路线的程序,使得对任何给定的起点和终点,可以找出最顺畅的路线。所谓“最顺畅”,首先是指中途经停站最少;如果经停站一样多,则取需要换乘线路次数最少的路线。
输入格式:
输入在第一行给出一个正整数N(≤100),即联盟公司的数量。接下来有N行,第i行(i=1,⋯,N)描述了第i家公司所提供的线路。格式为:
M S[1] S[2] ⋯ S[M]
其中M(≤100)是经停站的数量,S[i](i=1,⋯,M)是经停站的编号(由4位0-9的数字组成)。这里假设每条线路都是简单的一条可以双向运行的链路,并且输入保证是按照正确的经停顺序给出的 —— 也就是说,任意一对相邻的S[i]和S[i+1](i=1,⋯,M−1)之间都不存在其他经停站点。我们称相邻站点之间的线路为一个运营区间,每个运营区间只承包给一家公司。环线是有可能存在的,但不会不经停任何中间站点就从出发地回到出发地。当然,不同公司的线路是可能在某些站点有交叉的,这些站点就是客户的换乘点,我们假设任意换乘点涉及的不同公司的线路都不超过5条。
在描述了联盟线路之后,题目将给出一个正整数K(≤10),随后K行,每行给出一位客户的需求,即始发地的编号和目的地的编号,中间以一空格分隔。
输出格式:
处理每一位客户的需求。如果没有现成的线路可以使其到达目的地,就在一行中输出“Sorry, no line is available.”;如果目的地可达,则首先在一行中输出最顺畅路线的经停站数量(始发地和目的地不包括在内),然后按下列格式给出旅行路线:
Go by the line of company #X1 from S1 to S2.
Go by the line of company #X2 from S2 to S3.
......
其中Xi
是线路承包公司的编号,Si
是经停站的编号。但必须只输出始发地、换乘点和目的地,不能输出中间的经停站。题目保证满足要求的路线是唯一的。
输入样例:
4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
4
3011 3013
6666 2001
2004 3001
2222 6666
输出样例:
2
Go by the line of company #3 from 3011 to 3013.
10
Go by the line of company #4 from 6666 to 1306.
Go by the line of company #3 from 1306 to 2302.
Go by the line of company #2 from 2302 to 2001.
6
Go by the line of company #2 from 2004 to 1204.
Go by the line of company #1 from 1204 to 1306.
Go by the line of company #3 from 1306 to 3001.
Sorry, no line is available.
题意:
给定n条路线,每个路线都由k个点组成,并且每条路线是能够双向通行的,同时如果同一个点在不同路线都出现的话,可以从一条路线换乘到另一条路线。有k次询问,每次给出起点和终点,求出一条途径点数最少的路线,如果存在多条路线,则输出换乘次数最小的那一条,如果两个地点不可达,则输出无解
思路:
深度优先搜索
用数组模拟邻接表把每条路线中相邻的两个点“连上”一条无向边,这样题目就转化为了图的遍历了
但麻烦的是怎么表示不同的线路,因为还要计算换乘的次数。这里可以用int类型的二维数组line去存下每条路线相邻的两个点的路线编号是多少。比如题目中的“4 6666 8432 4011 1306” 就可以依次表示为,line[6666][8432] = 4,line[8432][4011] = 4,line[4011][1306] = 4。这样就能在O(1)的时间复杂度下快速查询一条路径之中相邻的两个站点是否为同一个路线的,是否存在换乘等。
//7-14 周游世界 (30 分)
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e4 + 10 , INF = 1e9;
bool vis[N];
int line[N][N]; //储存两个站点之间是否在第几条线路,0则表示不在一条线路
int n , m , k , pre , temp;
vector< vector<int> > v(N); //储存每个站点的可达点
vector<int> path , tempPath;
int transferCnt(vector<int> path) //计算一条路径的换乘次数
{
int cnt = -1 , preLine = 0; //初始化次数为-1(第一次必然更新) 上一条路径为 0
for(int i = 1 ; i < path.size() ; i++)
{
if(line[path[i - 1]][path[i]] != preLine) //两个相邻点不在上一条路线上,说明换乘了
cnt++;
preLine = line[path[i - 1]][path[i]];
}
return cnt;
}
void dfs(int u , int end , int cnt , int &minCnt , int &minTransfer)
{
if(cnt > minCnt) //如果途径站点数更大,必然不是最优解
return ;
if(u == end) //到达终点
{
if(cnt < minCnt || transferCnt(tempPath) < minTransfer) //途径站点数更小或换乘次数更小
{
minCnt = cnt;
minTransfer = transferCnt(tempPath);
path = tempPath;
}
return ;
}
for(int i = 0 ; i < v[u].size() ; i++)
{
if(!vis[v[u][i]])
{
vis[v[u][i]] = true;
tempPath.push_back(v[u][i]);
dfs(v[u][i] , end , cnt + 1 , minCnt , minTransfer);
tempPath.pop_back();
vis[v[u][i]] = false;
}
}
}
int main()
{
cin>>n;
for(int i = 0 ; i < n ; i++)
{
cin>>m>>pre; //先输入一个起点,保证待会一定是两个相邻点
for(int j = 1 ; j < m ; j++)
{
cin>>temp;
v[pre].push_back(temp) , v[temp].push_back(pre); //存入无向图
line[pre][temp] = line[temp][pre] = i + 1; //记录路线编号
pre = temp; //变量左移
}
}
cin>>k;
while(k--)
{
int a , b;
cin>>a>>b;
int minCnt = INF , minTransfer = INF; //途径点数和换乘次数置为INF
tempPath.clear();
tempPath.push_back(a);
vis[a] = true;
dfs(a , b , 0 , minCnt , minTransfer);
vis[a] = false;
if(minCnt == INF) //两点不可达
{
cout<<"Sorry, no line is available."<<endl;
continue;
}
cout<<minCnt<<endl;
int preLine = 0 , preTransfer = a;
for(int j = 1 ; j < path.size() ; j++)
{
if(line[path[j - 1]][path[j]] != preLine) //两个相邻点不在上一条路线上,说明换乘了
{
if(preLine) //排除不输出起点
printf("Go by the line of company #%d from %04d to %04d.\n"
, preLine , preTransfer , path[j - 1]);
preLine = line[path[j - 1]][path[j]];
preTransfer = path[j - 1];
}
}
printf("Go by the line of company #%d from %04d to %04d.\n" , preLine , preTransfer , b);
}
return 0;
}