1. 程式人生 > >Ideal Path, NEERC 2010, UVa1599 (題解+隨機測試資料)

Ideal Path, NEERC 2010, UVa1599 (題解+隨機測試資料)

題意簡述:一張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;
}