题意简述:一张n个顶点(1~n)m条边的无向图,每条边有颜色(用整数表示),保证存在从1到n的路径。要求给出从1出发到达n的最优路径:经过的边数最少,如果存在多种这样的走法,则取其中经过边的颜色序列按字典序最小的路径。输出这一颜色序列。
分析:m的上限为200000,用BFS求最短路径,时间复杂度为O(m)。为了先得到所有的最短路径,从节点n开始“倒序”BFS,
记录下每个节点i走到n的最短路程dis[i]。这样从1出发按每步dis减少1的路径走,必然能沿所有的最短路到达n。记录下dis以后,从节点1开始BFS。存下每一步“减1走法”中颜色最小(可能有多种)的走法(具体实现见代码中的vector<int>next和vector<int>newnext),将颜色序列存到ans数组中最后打印。
代码:
//Ideal Path
//Yhq
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#define maxn 100005
#define maxm 200005
#define inf 1<<30
using namespace std;
int n, m;
struct edge {
int node1, node2, color;
edge(int n1, int n2, int n3): node1(n1), node2(n2), color(n3) {}
};
bool vis[maxn];
int dis[maxn];
vector<int> graph[maxn], ans; //graph[i][j]=p, 则edges[p].node1==i; //ans记录最短路径颜色
vector<edge> edges;
void reset() {
memset(vis, false, sizeof(vis));
memset(dis, inf, sizeof(dis));
for (int i=0; i<maxn; ++i) graph[i].clear();
edges.clear();
ans.clear();
}
void build (int n1, int n2, int c) {
edges.push_back(edge(n1,n2,c));
graph[n1].push_back( (edges.size()-1) );
}
//从终点n开始倒着bfs, 记录每个节点到终点的最短路程dis
void rev_bfs() {
vis[n]=true;
dis[n]=0;
queue<int> que;
que.push(n);
while (!que.empty()) {
int last=que.front();
que.pop();
for (int i=0; i<graph[last].size(); ++i) {
edge ed = edges[graph[last][i]];
if (!vis[ed.node2]) {
vis[ed.node2]=true;
dis[ed.node2]=dis[last]+1;
que.push(ed.node2);
}
}
}
printf("%d\n",dis[1]);
}
// 从起点0开始, 找所有dis下降为1的路径,从中选择颜色编号最小的路径前进
// (多条路径颜色相同时全部要搜索下一层比较),直到终点n.
void bfs() {
vector<int> next;
next.push_back(1);
vis[1]=true;
for (int i=1; i<=dis[1]; ++i) {
int min_c=inf;
for (int j=0; j<next.size(); ++j) {
int k=next[j];
for (int p=0; p<graph[k].size(); ++p) {
int tmp=graph[k][p];
if (!vis[edges[tmp].node2] && dis[edges[tmp].node2]-dis[k]==-1) {
min_c= min (min_c, edges[tmp].color);
}
}
}
ans.push_back(min_c);
vector<int> newnext;
for (int j=0; j<next.size(); ++j) {
int k=next[j];
for (int p=0; p<graph[k].size(); ++p) {
int tmp=graph[k][p];
if (!vis[edges[tmp].node2] && dis[edges[tmp].node2]-dis[k]==-1 && edges[tmp].color==min_c) {
newnext.push_back(edges[tmp].node2);
vis[edges[tmp].node2]=true;
}
}
}
next=newnext;
}
}
int main () {
//freopen("IdealPath.txt","w",stdout);
while (scanf ("%d%d",&n,&m)==2) {
reset();
for (int i=1; i<=m; ++i) {
int n1, n2, c;
scanf("%d%d%d",&n1,&n2,&c);
build(n1,n2,c);
build(n2,n1,c);
}
rev_bfs();
memset(vis,false,sizeof(vis));
bfs();
for (int i=0; i<ans.size(); ++i) {
printf("%d",ans[i]);
if (i<ans.size()-1) printf(" ");
else printf("\n");
}
}
return 0;
}
测试数据生成代码:
修改数值上限MAXN, MAXM, COLOR, TESTNUM;测试数据会生成到IDEALPATH.txt文件。
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<iostream>
#define TESTNUM 100
#define MAXN 100
#define MAXM 200
#define COLOR 10
using namespace std;
int main () {
freopen("IDEALPATH.txt","w",stdout);
srand ( (unsigned int) time(NULL) );
//code here
int testnum=TESTNUM;
while (testnum--) {
int colornum=rand()%COLOR+2;
int n=rand()%MAXN + 2;
int m=rand()%MAXM +3;
cout<<n<<" "<<m<<endl;
int t1=1, t2=2;
while (t2<n) {
if (m==1) {
cout<<t1<<" "<<n<<" "<<rand()%colornum<<endl;
--m;
break;
}
t2=rand()%(n-t1)+t1+1;
cout<<t1<<" "<<t2<<" "<<rand()%colornum<<endl;
--m;
t1=t2;
}
for (int i=1; i<=m; ++i) {
cout<<rand()%n+1<<" "<<rand()%n+1<<" "<<rand()%colornum<<endl;
}
cout<<endl;
}
return 0;
}