题目描述:
给定一个n个点m条边的无向图,每条边上都涂有1种颜色。求点1到点n的一条路径,使得经过的边数最少,在此前提下,经过边的颜色序列最小。可能有自环与重边。输入保证至少存在一条连接1和n的道路。
输入:输入共m+1行
第一行2个整数:n和m。
以后m行,每行空格隔开的3个整数ai,bi,ci,表示在ai,bi之间有一条颜色为ci的道路。
输出:输出共两行
第一行1个正整数k,表示1到n至少需要经过k条边。
第二行包含k个空格隔开的正整数,表示从1到n依次经过的边的颜色。
解题思路:
这个题目可以分成两问来看:首先,找两个结点之间的最少边数;之后,在最少边数的所有路径中选出颜色序列最短的那一条路径。都适合用BFS,反向标高、正向找路径。
解题步骤:
反向标高:
1、用链式前向星存储这个无向图,从n出发反向广度优先遍历,对每个结点标高;
2、1的高度就是从1到n最少的边数,也就是题的第一问。
int head[maxn];
int high[maxn];
class Edge
{
public:
int to;
int weight;
int next;
};
Edge edge[maxm];
void InsertEgdg(int u, int v, int w)
{
edge[++cnt].to = v;
edge[cnt].weight = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void CreateGraph()
{
int u, v, w;
cin >> n >> m;
for (int i = 0; i < m; i++)
{
cin >> u >> v >> w;
InsertEgdg(u, v, w);
InsertEgdg(v, u, w);
}
}
void ReverseBFS()
{
memset(high, 0, sizeof(high));
int u = 0;
high[n] = 0;
visited[n] = true;
q1.push(n);
while (!q1.empty())
{
u = q1.front();
q1.pop();
for (int i = head[u]; i; i = edge[i].next)
{
if (visited[edge[i].to])
{
continue;
}
high[edge[i].to] = high[u] + 1;
visited[edge[i].to] = true;
q1.push(edge[i].to);
}
}
}
正向找路径:
1、首先需要建立3个队列,q1用来保存BFS需要的邻接点号,q2用来保存最小色号,q3用来保存色号序列最小边关联的邻接点号;
2、把1的邻接点中高度-1的结点入队,结点号如q1,色号入q2;
3、首先找出扩散一圈的色号最小值:q1和q2同步出队,在q3中保存色号最小边的邻接点的下标,找到色号最小边时,可以回答第二问的一个空,但此时最小边对应的邻接点可能不唯一。
minw = 0x7fffffff;
while (!q1.empty()) //找扩散第一圈的最小值
{
v = q1.front();
q1.pop();
w = q2.front();
q2.pop();
if (w < minw) //更新色号最小值
{
while (!q3.empty())
{
q3.pop();
}
minw = w;
}
if (w == minw) //同最小值放入队列
{
q3.push(v);
}
}
cout << minw << " ";
4、 只在色号序列最小的邻接点继续扩散,在q3中找到高度-1的邻接点,结点号入q1、权值入q2.
整体代码实现:
#include <iostream>
using namespace std;
#include <queue>
#define maxn 100000
#define maxm 200000
int n, m, w, cnt;
bool visited[maxn];
queue<int> q1, q2, q3;
int head[maxn];
int high[maxn];
class Edge
{
public:
int to;
int weight;
int next;
};
Edge edge[maxm];
void InsertEgdg(int u, int v, int w)
{
edge[++cnt].to = v;
edge[cnt].weight = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void CreateGraph()
{
int u, v, w;
cin >> n >> m;
for (int i = 0; i < m; i++)
{
cin >> u >> v >> w;
InsertEgdg(u, v, w);
InsertEgdg(v, u, w);
}
}
void ReverseBFS()
{
memset(high, 0, sizeof(high));
int u = 0;
high[n] = 0;
visited[n] = true;
q1.push(n);
while (!q1.empty())
{
u = q1.front();
q1.pop();
for (int i = head[u]; i; i = edge[i].next)
{
if (visited[edge[i].to])
{
continue;
}
high[edge[i].to] = high[u] + 1;
visited[edge[i].to] = true;
q1.push(edge[i].to);
}
}
}
void BFS()
{
int u = 0, v = 0, w = 0;
int minw = 0;
memset(visited, false, sizeof(visited));
visited[1] = true;
for (int i = head[1]; i; i = edge[i].next) //找1的高度减1邻接点
{
if (high[edge[i].to] == high[1] - 1)
{
q1.push(edge[i].to);
q2.push(edge[i].weight);
}
}
while (!q1.empty())
{
minw = 0x7fffffff;
while (!q1.empty()) //找扩散第一圈的最小值
{
v = q1.front();
q1.pop();
w = q2.front();
q2.pop();
if (w < minw) //更新色号最小值
{
while (!q3.empty())
{
q3.pop();
}
minw = w;
}
if (w == minw) //同最小值放入队列
{
q3.push(v);
}
}
cout << minw << " ";
while (!q3.empty()) //只在色号序列最小的邻接点继续扩散
{
u = q3.front();
q3.pop();
if (visited[u])
{
continue;
}
visited[u] = true;
for (int i = head[u]; i; i = edge[i].next)
{
v = edge[i].to;
if (high[v] == high[u] - 1) //只在高度-1的邻接点扩散
{
q1.push(v);
q2.push(edge[i].weight);
}
}
}
}
}
int main()
{
CreateGraph();
ReverseBFS();
cout << high[1] << endl;
BFS();
cout << endl;
return 0;
}