1. 程式人生 > >1599 Ideal Path (一遍樹上BFS)

1599 Ideal Path (一遍樹上BFS)

 UVA - 1599  Ideal Path 

New labyrinth attraction is open in New Lostland amusement park. The labyrinth consists of n rooms connected by m passages. Each passage is colored into some color ci. Visitors of the labyrinth are dropped from the helicopter to the room number 1 and their goal is to get to the labyrinth exit located in the room number n

.

Labyrinth owners are planning to run a contest tomorrow. Several runners will be dropped to the room number 1. They will run to the room number n writing down colors of passages as they run through them. The contestant with the shortest sequence of colors is the winner of the contest. If there are several contestants with the same sequence length, the one with the ideal path is the winner. The path is the ideal path if its color sequence is the lexicographically smallest among shortest paths.

Andrew is preparing for the contest. He took a helicopter tour above New Lostland and made a picture of the labyrinth. Your task is to help him find the ideal path from the room number 1 to the room number n that would allow him to win the contest.


Note:

A sequence (a1, a2,..., ak) is lexicographically smaller than a sequence (b

1, b2,..., bk) if there exists i such that ai < bi, and aj = bj for all ji.

Input 

The input file contains several test cases, each of them as described below.

The first line of the input file contains integers n and m -- the number of rooms and passages, respectively (2$ \le$n$ \le$100000, 1$ \le$m$ \le$200000). The following m lines describe passages, each passage is described with three integer numbers: ai, bi, and ci -- the numbers of rooms it connects and its color (1$ \le$ai, bi$ \le$n, 1$ \le$ci$ \le$109). Each passage can be passed in either direction. Two rooms can be connected with more than one passage, there can be a passage from a room to itself. It is guaranteed that it is possible to reach the room number nfrom the room number 1.

For each test case, the output must follow the description below.

The first line of the output file must contain k -- the length of the shortest path from the room number 1 to the room number n. The second line must contain k numbers -- the colors of passages in the order they must be passed in the ideal path.

4 6 
1 2 1
1 3 2
3 4 3
2 3 1
2 4 4
3 1 1
2 
1 3

白書上的原題,題意是求出最短路中顏色字典序也最小的那條路徑,白書上的意思是先進行一次逆向DFS把節點的深度跑出來再正向DFS總去走那個深度深一格節點的顏色小的路。但是他也同時提到,可以僅僅使用一次逆向DFS做出來。我和同學研究了很久,我選擇了正向DFS使用了兩個優先佇列共同維護路徑上一個深度的顏色字典序排名,和這個深度的顏色的字典序。但是因為迷之原因,雖然能過樣例,但是一直RE?後來選擇了了大佬同學的逆向DFS法。(等下再講)這裡也貼出來,看看有沒有大牛幫我看看那裡有問題:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>

using namespace std;
struct node{
	int point;
	int befo;
	int rank;
	int step;
	int last;
	int no;
	friend bool operator<(node a,node b)
	{
		if(a.step==b.step)
		{
			if(a.rank==b.rank)
			{
				return a.befo>b.befo;
			}
			return a.rank>b.rank;
		}
		return a.step>b.step;
	}
};
map<long long,int> flag;
priority_queue<node> q,p;
int tot;
const int size=1e5+5;
int roar[size];
node ex[10*size];
int n,m;
int fir[size],nxt[4*size],beg[4*size],en[4*size],col[4*size];
void edge_build(int b,int e,int c)
{
	beg[tot]=b;
	en[tot]=e;
	col[tot]=c;
	nxt[tot]=fir[b];
	fir[b]=tot++;
}
long long Hash(long long x,int y)
{
	return x*size+y;
}
int ccnt=0;
node bfs()
{
	while(!q.empty()) q.pop();
	node u;
	u.point=1;
	u.step=0;
	
	u.rank=1;
	u.no=0;
	ex[0]=u;
	q.push(u);
	
	while(!q.empty())
	{
		while(!q.empty())
		{
			node s=q.top();
			q.pop();
			if(s.point==n) return s;
			for(int i=fir[s.point];i!=-1;i=nxt[i])
			{
				if(!flag.count(Hash(beg[i],en[i])))
				{
					node t;
					t.point=en[i];
					t.step=s.step+1;
					t.no=ccnt;
					t.last=s.no;
					//cout<<en[i]<<' '<<beg[i]<<endl;
					t.befo=col[i];
					t.rank=s.rank;
					
					flag[Hash(beg[i],en[i])]=1;
					if(ccnt>=10*size) return t;
					ex[ccnt++]=t;
					p.push(t);
				}
			}
		}
			int cnt=0,estrank=0,estbefo=0;
			while(!p.empty())
			{
				node temp=p.top();
				p.pop();
				if(temp.rank>estrank)
				{
					cnt++;
					estrank=temp.rank;
					estbefo=0;
				}
				else if(estrank==temp.rank&&temp.befo>estbefo)
				{
					cnt++;
					estbefo=temp.befo;
				}
				temp.rank=cnt;
				q.push(temp);
			}
		
	}
}
int main()
{
	while(cin>>n>>m)
	{
		tot=0;
		flag.clear();
		int i;
		ccnt=1;
		memset(fir,-1,sizeof(fir));
		memset(nxt,0,sizeof(nxt));
		memset(roar,0,sizeof(roar));
		memset(ex,0,sizeof(ex));
		for(i=0;i<m;i++)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			edge_build(a,b,c);
			edge_build(b,a,c);
		}
		node pp=bfs();
		int cnt=0;
		cout<<pp.step<<endl;
		//cout<<pp.last<<endl;
		for(i=pp.no;i!=0;i=ex[ex[i].last].no)
		{
			//cout<<i<<endl;
			roar[cnt++]=ex[i].befo;
		}
		//cout<<'x'<<endl;
		for(i=cnt-1;i>=0;i--)
		{
			printf("%d",roar[i]);
			if(i!=0) printf(" ");
			else puts("");
		}
	}
	return 0;
}

同學的想法是逆向DFS,如果在搜尋的過程中遇到了已經搜尋過的點,且其到目標節點的距離與其通過當前節點向目標節點的長度相等則不斷向目標節點更新,找出字典序更小的那條路,這就是這個點到目標節點字典序最小且最短的路,如未搜尋過這個點,則記錄上這個點到目標節點的距離,最終達到根節點,詳情見程式碼:

AC程式碼:

#include<iostream>
#include<queue>
#include<cstdio>
#include<set>
#include<cstring>
using namespace std;
struct node{
	int b,e,c;
};
typedef node edge;
const int size=1e5+5;
edge roar[4*size];
int first[size],nxt[size*4]; 
int tot=0;
int n,m;
set<int> flag;
int len[size];
int befo[size];
void edge_build(int b,int e,int c)
{
	roar[tot].b=b;
	roar[tot].e=e;
	roar[tot].c=c;
	nxt[tot]=first[b];
	first[b]=tot++;
}
queue<int> q;
void bfs()
{
	flag.clear();
	q.push(n);
	flag.insert(n);
	len[n]=0;
	while(!q.empty())
	{
		int s=q.front();
		q.pop();
		for(int i=first[s];i!=-1;i=nxt[i])
		{
			int b=roar[i].b;
			int e=roar[i].e;
			if(flag.count(e))
			{
				if(len[s]+1==len[e])
				{
					int j=befo[e];
					int k=i;
					while(j!=k&&roar[j].c==roar[k].c) j=befo[roar[j].b],k=befo[roar[k].b];
					if(roar[j].c>roar[k].c) befo[e]=i;
				}
			}
			else
			{
				flag.insert(e);
				befo[e]=i;
				len[e]=len[s]+1;
				q.push(e);
			}
		}
	}
}

int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		memset(first,-1,sizeof(first));
		memset(nxt,0,sizeof(nxt));
		tot=0;
		for(int i=0;i<m;i++)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			edge_build(a,b,c);
			edge_build(b,a,c);
		}
		bfs();
		cout<<len[1]<<endl;
		for(int i=befo[1];len[1]--;i=befo[roar[i].b])
		{
			cout<<roar[i].c;
			if(len[1]) printf(" ");
			else printf("\n");
		}
	}
}