天天看点

字典序最小的最短路问题描述:输入样例:输出样例:限制范围:分析:参考程序:

问题描述:

给出n和m,有n个点,m条双向边,1为起点,n为终点,每条边都有一个权值,经过每一条边的时间都为1,求从起点到终点既要时间最少,又要权值组成的序列的字典序最小的一条路径.

输入样例:

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

输出样例:

2 1 3

限制范围:

50%,n<=100 100%,n<=100000,m<=200000

分析:

又是一道双关键字限制结果最优性的问题,此类问题一般思路就是枚举一个,计算最优另一个。 对于这道题,我们可以先用SPFA算出从终点开始到每个点的距离,然后从起点开始,每一步都选择权值最小的即可。其实也不是很复杂。

参考程序:

#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=610000;
const int INF=0x3f3f3f3f;
struct Edge{
	int j,v,next;
}e[maxn];
int EdgeCnt=0;
int d[maxn],a[maxn];
bool inq[maxn];
queue<int> Q;
int n,m;
void addedge(int u,int v,int w){
	int p=++EdgeCnt;
	e[p].j=v;e[p].v=w;e[p].next=a[u];
	a[u]=p;
}
int main(){
	freopen("ideal.in","r",stdin);
	freopen("ideal.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=0;i<m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,w);
		addedge(v,u,w);
	}
	memset(d,0x3f,sizeof(d));
	Q.push(n);
	inq[n]=true;
	d[n]=0;
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		for (int p=a[u];p;p=e[p].next){
			int j=e[p].j;
			if (d[j]>d[u]+1){
				d[j]=d[u]+1;
				if (!inq[j]){
					inq[j]=true;
					Q.push(j);
				}
			}
		}
		inq[u]=false;
	}
	printf("%d\n",d[1]);
	set<int> now,next;
	now.insert(1);
	for (int dist=d[1]-1;dist>=0;dist--){
		int col=INF;
		for (set<int>::iterator u=now.begin();u!=now.end();u++){
			for (int p=a[*u];p;p=e[p].next){
				int j=e[p].j,v=e[p].v;
				if (d[j]==dist && v<=col){
					if (v<col){
					    col=v;
					    next.clear();
					    next.insert(j);
				    }else if (next.find(j)==next.end())next.insert(j);
				}
			}
		}
		printf("%d ",col);
		now=next;next.clear();
	}
	return 0;
}