分析
- 因为站号从0000-9999,如果直接设置邻接表
table[10000][10000]
来表示结点之间的连通关系,显然内存超限。 - 考虑以站作为结点,设置结构体,对于每个结点,记录和它相邻的结点。(即通过邻接表表示图)
- 每个站可能和多个站相连,故在结构体内设置一个数组,记录所有邻站编号。
关键点
利用DFS不难得出所求的路径,关键是怎样表示一些站点属于哪条线路。
由题目描述:
Each station interval belongs to a unique subway line.
故两个相邻的站唯一地属于某一条线路。
为了记录两个邻站组成的数对属于哪条线路,作以下处理:
- 在每个结构体结点中设置一个
unordered_set
,记录这个结点属于哪些线路。 - 另设置一个数组
unordered_map<int, int> line[101];
,记录每条线路的信息。数组的每一项表示一条线路,且每一项是一个unordered_map
,其中键表示邻站对的其中一个结点,值表示邻站对的另一个结点。
通过以上两个结构,就可以通过两个相邻结点找到它们所属的线路:对于两个结点,遍历其中一个结点所属的全部线路,如果一条线路包含这两个结点组成的键值对,则说明两个结点属于这条线路。
另需注意:
- 注意
visited
数组设置成false的时机。 - 在DFS过程中维护一个数组表示当前遍历的结点,在回溯时注意删除相应元素。
- DFS结束后得到所求路径的全部结点,可以通过邻站对求出其所属线路,进而进行输出。
#include<cstdio>
#include<vector>
#include<algorithm>
#include<unordered_set>
#include<unordered_map>
using namespace std;
const int inf = 0x7fffffff;
int lineNum, queryNum;
struct node {
int stationId; //站号
vector<int> nextStations; //直接相连的站
unordered_set<int> belongedLines; //属于的线路
}table[10000];
//unordered_set<int> line[101]; //记录每条线包含的站
unordered_map<int, int> line[101]; //每条线路号指向一个map,每个map保存这条线路的全部站号对
int getLineId(int s1, int s2) { //返回相邻两站属于哪条线路
if (s1 == -1 || s2 == -1)
return -1;
for (auto it = table[s1].belongedLines.begin();it != table[s1].belongedLines.end();it++) {
if (line[*it][s1] == s2 || line[*it][s2] == s1)
return *it;
}
}
bool visited[10000];
vector<int> route;
int minStationCount, minTransferCount, destination;
void dfs(int cur, int pre, int preLine, int transferCount, vector<int> curRoute) {
curRoute.push_back(cur);
visited[cur] = true;
if (curRoute.size() > minStationCount) { //剪枝
curRoute.pop_back();
visited[cur] = false;
return;
}
int curLine = getLineId(pre, cur);
if (curLine != preLine)
transferCount++;
if (cur == destination) {
if (curRoute.size() < minStationCount || (curRoute.size() == minStationCount && transferCount < minTransferCount)) {
route = curRoute;
minStationCount = curRoute.size();
minTransferCount = transferCount;
}
curRoute.pop_back();
visited[cur] = false;
return;
}
for (int i = 0;i < table[cur].nextStations.size();i++) {
if (!visited[table[cur].nextStations[i]]) {
dfs(table[cur].nextStations[i], cur, curLine, transferCount, curRoute);
}
}
visited[cur] = false;
curRoute.pop_back();
}
int main() {
scanf("%d", &lineNum);
for (int i = 0;i < lineNum;i++) {
int stopNum;
scanf("%d", &stopNum);
int pre = -1;
for (int j = 0;j < stopNum;j++) {
int id;
scanf("%d", &id);
table[id].stationId = id;
table[id].belongedLines.insert(i + 1);
if (pre != -1) {
table[id].nextStations.push_back(pre);
table[pre].nextStations.push_back(id);
line[i + 1][pre] = id;
}
pre = id;
}
}
scanf("%d", &queryNum);
for (int i = 0;i < queryNum;i++) {
int source, dest;
scanf("%d %d", &source, &dest);
route.clear();
minStationCount = inf, minTransferCount = inf, destination = dest;
vector<int> temp;
dfs(source, -1, -1, 0, temp);
printf("%d\n", route.size() - 1);
int start = 0, end = 1;
while (end < route.size()) {
int curLine = getLineId(route[start], route[end]);
while (end + 1 < route.size() && getLineId(route[end], route[end + 1]) == curLine) {
end++;
}
printf("Take Line#%d from %04d to %04d.\n", curLine, route[start], route[end]);
start = end;
end = start + 1;
}
}
}
二刷:
用了错误的方法存两个邻站属于哪条线路:
unordered_map<int, unordered_set<int> > station2Line; //记录了一个站属于哪些线路
int getLine(int s1, int s2) {
for (int line : station2Line[s1]) {
if (station2Line[s2].count(line) != 0) {
return line;
}
}
}
两个邻站有可能同时属于多条相同的线路,如果仅看两个站都在哪条线路上,则不能找到这两个站属于哪条线路。
需要用两个邻站来作为键来记录它们属于的线路。设置二维数组station2Line
,station2Line[i][j]
就表示两邻站i
和j
属于的线路。
其次,因为图是成环的,需要设置unordered_set<int> visited
记录当前线路上的结点,避免死循环。但是要记得在dfs返回前清除当前结点,不然下次就不能从这个结点经过了。
AC代码:
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
using namespace std;
const int inf = 0x7fffffff;
unordered_map<int, unordered_set<int> > station;
int station2Line[10000][10000];
//两个站属于哪条线路
int getLine(int s1, int s2) {
return station2Line[s1][s2];
}
unordered_set<int> visited;
int minStationCount, minLineCount, dest;
vector<int> bestRoute;
void dfs(vector<int> route, int cur, int lineCount, int curLine) {
if (cur == dest) {
if (route.size() < minStationCount || (route.size() == minStationCount && lineCount < minLineCount)) {
minStationCount = route.size();
minLineCount = lineCount;
bestRoute = route;
}
return;
}
visited.insert(cur);
for (int nextStation : station[cur]) {
if (visited.count(nextStation) == 0) {
int nextLine = getLine(cur, nextStation);
int nextLineCount = lineCount;
if (nextLine != curLine) {
++nextLineCount;
}
route.push_back(nextStation);
dfs(route, nextStation, nextLineCount, nextLine);
route.pop_back();
}
}
visited.erase(cur);
}
int main() {
int numLine, numQuery;
cin >> numLine;
for (int i = 1; i <= numLine; i++) {
int n;
cin >> n;
int pre;
for (int j = 0; j < n; j++) {
int s;
cin >> s;
if (j != 0) {
station[pre].insert(s);
station[s].insert(pre);
station2Line[s][pre] = i;
station2Line[pre][s] = i;
}
pre = s;
}
}
cin >> numQuery;
for (int i = 0; i < numQuery; i++) {
int s1, s2;
cin >> s1 >> s2;
minStationCount = inf;
minLineCount = inf;
dest = s2;
visited.clear();
bestRoute.clear();
vector<int> temp;
temp.push_back(s1);
dfs(temp, s1, 0, -1);
cout << bestRoute.size() - 1 << endl;
int cur = 0, start = 0;
int curLine = getLine(bestRoute[0], bestRoute[1]);
while (cur < minStationCount - 1) {
while (cur + 1 < minStationCount && getLine(bestRoute[cur], bestRoute[cur + 1]) == curLine) {
++cur;
}
printf("Take Line#%d from %04d to %04d.\n", curLine, bestRoute[start], bestRoute[cur]);
if (cur + 1 < minStationCount) {
start = cur;
curLine = getLine(bestRoute[cur], bestRoute[cur + 1]);
}
}
}
}