uva UVA - 1599 Ideal Path

本文探讨了一种特殊的图遍历问题,即寻找两点间边数最少且边颜色字典序最小的路径。通过逆向BFS算法,文章详细介绍了如何高效求解这一问题,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给定n(<=100000)个点(1......n),m(<=200000)条边,每条边有个颜色值,试求从点1到点n经过的边数最少,在此前提下,经过的边数的颜色序列的字典树最小。

思路:如果只是边数最少直接一个bfs即可求之。现在多了个条件边数的颜色值序列的字典树最小,可以先选颜色值小的?不能,因为你不知道从颜色最小的走是否走的到,或者步数最短呢。

我们逆向思考,如果我们从终点bfs一下,记录每个点到终点的最短距离,然后我们从起点出发,每一步只往(距离-1)的节点走,因为,这样才能确保最短路径,然后,把距离相同的看作一层,从起点出发,刚开始第一次只有起点,往下一层所有节点中的最小颜色值走,没走一层确定的一个答案,如果该层最小颜色值有多个,我们不能确定到底走哪个,具体还要看下下层,因此,我们把最小颜色值所有节点看作新的一层,覆盖旧的,相当于起点从一个变成多个。这样迭代。


2.对于每个节点我们最多只放入队列一次,所以用个数组标记,不然TLE。

/*
wrong:
9 9
1 2 1
1 3 1
2 4 2
2 5 3
3 6 1
6 8 5
4 7 1
7 9 2
8 9 2
*/
 #include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int n,m;
int step[2*maxn];
int ans [2*maxn];
int vis[2*maxn];
int  T ;
const int INF = 1e9+5;
int ans_L;
struct Edge{
	int Next;
	int mark;
	Edge(int Next_ = -1,int mark_ =-1):Next(Next_),mark(mark_){}
};
vector<Edge> G[maxn];
queue<int > q;
void addEdge(int u,int v,int mark){
	G[u].push_back(Edge(v,mark) );
}
void bfs(){
	 int pre;
	 int i,t,len,edge;
	 while(!q.empty()) q.pop();
     q.push(n);
//     for(i=0;i<10;i++){
//     	printf("%d ",step[i]);
//	 }
     step[n] = 0;
     while(!q.empty()){   //给每个节点记录到目标节点的距离 
     	 pre = q.front();
     	 q.pop();
	     len =  G[pre].size();
	     //printf("len = %d ",len);
	     for(i=0; i<len; i++){
	     	 t = G[pre][i].Next;
	     	 //printf("%d %d\n",pre,t);
	     	 if(step[t] == -1){
	     	 	step[t] = step[pre] +1;
	     	 	q.push(t);
			  }
		 }	
	 }
}

void bfs_ans(){  /*思路有问题,有可能求出的答案不是一条路径
因为该方法是根据每一个数取其所有下一步节点的最小值,应该是取step值一样的所有下一步节点的最小值
因为我们需要找一条路径*/
	 int pre;
	 int i,t,len,edge,mini,j;
	
	 while(!q.empty()) q.pop();
	 vis[1] = 1;
	 q.push(1);
	 while(!q.empty()){  //从多条最短路径中寻找字典序最小的那条 
	    pre = q.front();
	 	q.pop();
	 	len = G[pre].size();
	 	mini = 1e9+5; 
	 	//ans[step[pre]] = mini = 1e9+5;  不能这么初始化,因为有可能之前ans[step[pre]]被赋值过 
		for(i=0;i<len;i++){
			if(step[G[pre][i].Next] == step[pre] - 1){
					mini = min (mini,G[pre][i].mark);			 
			}
		}                          
		for(i=0;i<len;i++){                  //根据最短的那条边,我们可以把可能经过的点存入队列,注意不要重复 
			if(step[G[pre][i].Next] == step[pre] - 1){
				if(G[pre][i].mark == mini && step[G[pre][i].Next] != 0){	 
			 		if(!vis[G[pre][i].Next]){   /*注释掉超时 */
					 	 vis[G[pre][i].Next] = !vis[G[pre][i].Next];
					 	  printf("%d %d\n",pre,G[pre][i].Next);
					 	  q.push(G[pre][i].Next);				
					 }
				}
			}
		}
		printf("#%d %d\n",step[pre],mini);
		ans[step[pre]] = min(mini,ans[step[pre]]);
	 }
}
void bfs_a(){
	int deep = step[1];
	int i,d,v,ch,L,len,pre,mini,j;
	vector<int > v1;
	vector<int > v2;
	v1.push_back(1);
	for(d=deep;d>=1;d--){  //floor
        len = v1.size();
//        for(i=0;i<len;i++){
//        	printf("%d ",v1[i]);
//		}
//		puts("!");
		mini = INF; 
		for(i=0;i<len;i++){  //pre
			pre = v1[i];
		//	printf("pre = %d\n",pre);
			L = G[pre].size();
			for(j=0;j<L;j++){ //ch
				ch = G[pre][j].Next;	
				//printf("ch = %d\n",ch);
				if(step[ch] == step[pre]-1 && G[pre][j].mark < mini){
				    mini = G[pre][j].mark;
				} 	
			}
		}
	//	printf("d = %d %d\n",d,mini);
		for(i=0;i<len;i++){
			pre = v1[i];
			L = G[pre].size();
			for(j=0;j<L;j++){ //ch
				ch = G[pre][j].Next;	
				if(step[ch] == step[pre]-1 && G[pre][j].mark == mini && !vis[ch]){    //vis数组去掉还是TLE 
				   vis[ch] = 1;  
				    v2.push_back(ch);
				} 	
			}
		}
		v1 = v2;
		v2.clear();
	//	printf("mini %d %d\n",d,mini);
		ans[d] = mini;
	}
} 
void pritf(int cur){
	int len  = G[cur].size();
	for(int i=0;i<len;i++){
		printf("%d %d\n",cur,G[cur][i].Next);
	}
	system("pause");
}
int main()
{
	int i,j,k;
	int u,v,mark;
	while(scanf("%d%d",&n,&m) == 2){
		for(i=0;i<maxn;i++){
			G[i].clear();
		}
		for(i=0;i<m;i++){
			scanf("%d%d%d",&u,&v,&mark);
			addEdge(u,v,mark);		
			addEdge(v,u,mark);	
		}
		//pritf(1);
	    memset(step,-1,sizeof(step));
		bfs();
//		for(int i=1;i<=n;i++){
//			printf("%d %d\n",i,step[i]);
//		}
	//	system("pause");
		ans_L =  step[1];
	    memset(vis,0,sizeof(vis));
		bfs_a();
		printf("%d\n%d",ans_L,ans[ans_L]);
		for(i=ans_L-1;i>=1;i--){
			printf(" %d",ans[i]);
		}
		puts("");
	}
	return 0;
}

!!!!!

注意点:1. 根据每个节点来扩展其所有孩子,一个queue来根据每个节点距离值来更新答案,对于每个节点,我们找到其最小的颜色值相连的孩子节点,然后把该节点放入队列,利用该颜色值更新答案。但是这样做问题在于,最终的找到的路径不是联通的,只是确保一层接着一层,以下数据为例,从1号出发,到达2,3号节点的颜色值相同,更新a【0】,然后把2,3放入队列,接着以2为起点,4,5号节点选择最小的颜色值4号节点,更新a【1】,以3起点找到经过颜色值最小边的孩子,更新a【1】,然后以4号节点为根,更新a【2】,!!!!错了,因为我们是走的3好节点,所以四号节点没有价值了。

/*
wrong:
9 9
1 2 1
1 3 1
2 4 2
2 5 3
3 6 1
6 8 5
4 7 1
7 9 2
8 9 2


*/





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值