SCU OJ_4444_ Travel_最短路(大数据)

该博客探讨了在存在两种交通方式(公路和铁路)的n个城市中,从城市1到城市n的最短路径问题。文章指出,由于所有城市相互连通,所以存在两种情况:直接通过某条路线到达或间接到达。针对这种情况,博主提出了使用BFS(广度优先搜索)解决直接到达的情况,并解释了为何SPFA(Shortest Path Faster Algorithm)在处理间接到达时可能超时。为了解决这个问题,博主建议利用集合和队列来优化搜索过程,确保不超过n-1次连接,从而找到间接到达n的最短路径。

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

I - Travel
Time Limit:0MS     Memory Limit:0KB     64bit IO Format:%lld & %llu
Submit Status

Description
Travel

The country frog lives in has n
towns which are conveniently numbered by 1,2,…,n

.

Among n(n?1)2
pairs of towns, m of them are connected by bidirectional(双向的) highway(公路), which needs a minutes to travel.
The other pairs are connected by railway, which needs b

minutes to travel.

Find the minimum(最小的) time to travel from town 1
to town n

.
Input

The input(投入) consists of multiple tests. For each test:

The first line contains 4
integers n,m,a,b (2≤n≤105,0≤m≤5?105,1≤a,b≤109). Each of the following m lines contains 2 integers ui,vi,
 which denotes(表示) cities ui and vi are connected by highway(公路). (1≤ui,vi≤n,ui≠vi

).
Output

For each test, write 1

integer(整数) which denotes(表示) the minimum(最小的) time.
Sample Input

3 2 1 3
1 2
2 3
3 2 2 3
1 2
2 3

Sample Output

2

3


题意:有n个城市,编号为1~n,每个城市都相互连通,其中有m对城市通过公路连通,其他的城市通过铁路连通,经过公路的时间为a,经过铁路的时间为b,问从1到达n的时间最短为多少.

题解:求最短路问题,但是这道题中,每个点都是相连通的,不过连通的边有2种,那么从1走到n就可以分成2种情况:

①1和n通过公路相连,直接到达n的时间为a,那么就要考虑是直接用时a到达n,还是经过若干个b到达n
②1和n通过铁路相连,直接到达n的时间为b,那么就要考虑是直接用时b到达n,还是经过若干个a到达n


      由于数据量比较大,我们肯定要用邻接表 建图。 对于第二种情况我们用邻接表加 spfa 跑一遍 完全能求出来但第一种情况如何建图,如何求,一直思考不出来,看了这位大神用 集合实现 。用搜索的形式求出 补图,并同时求最短路。感觉很巧妙,真是太厉害了。

大神 博客 http://blog.youkuaiyun.com/lvshubao1314/article/details/48848319



因此我们就能针对1与n的连通情况进行bfs,如果间接到达n的时间cnt要小于直接通过相连的路到达的时间t,那么最短时间就是cnt,反之,答案则为t对于情况

②我们很容易想到用SPFA做,不过情况①的边有n(n-1)/2-m条,显然用SPFA会超时.我们可以发现,要间接到达n,通过的点数绝对不会超过n-1,因为要走的路不能构成环,那么我们可以用set储存2~n,当目前点的编号为u,搜索与u通过a或者b相连的点v,存入队列,然后将v从set中删除,最后就能得到间接到达n点的最短路


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>//相同的数 不共存 
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
long long dist[100010];
int head[100010];
bool vis[100010];
set<int>st,ts; //定义 两个集合 
set<int>::iterator it;      //迭代器 

struct Node
{
	int v;
	int w;
	int next;
}edge[1000010];
long long a,b;
int n,m,num;
void add_edge(int u,int v)
{
	edge[num].v = v;
	edge[num].w = a;
	edge[num].next = head[u];
	head[u] = num++; 
}
long long spfa()
{
	int u,v,i,w;
	queue<int> q;
	q.push(1);
	memset(dist,inf,sizeof(dist));
	memset(vis,false,sizeof(vis));
	dist[1] = 0;
	vis[1] = true;
	while(!q.empty())
	{
		u = q.front();
		q.pop();
		vis[u] = false;
		for(i=head[u];i!=-1;i=edge[i].next)
		{
			v = edge[i].v;
			w = edge[i].w;
			if(dist[v]>dist[u]+w)
			{
				dist[v] = dist[u] + w;
				if(!vis[v])
				{
					vis[v] = true;
					q.push(v);
				}	
			}
		}
	}
	return dist[n]<b ? dist[n]:b;
}
long long bfs()
{
	int i,u,v;
    dist[n]=inf;
    st.clear(); ts.clear();
    for(i=2;i<=n;i++) 
    { 
		st.insert(i);//把数放入集合中 
	} 
    queue<int>q;
    q.push(1);
    dist[1]=0;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            v=edge[i].v;
            if(st.count(v)==0)//判断当前的数 是否在集合中 
			{
				continue;
			}
            st.erase(v);//从当前集合中移除 
			ts.insert(v); 
        }
        for(it=st.begin();it!=st.end();it++)
        {
            q.push(*it);
            dist[*it]=dist[u]+1;
        }
        st.swap(ts); //把两集合内的元素互换 
        ts.clear();  //当前集合内的元素删去 
    }
    return dist[n]*b<a ? dist[n]*b:a;
}
int main()
{
	while(scanf("%d%d%lld%lld",&n,&m,&a,&b)!=EOF)
	{
		int i,j,u,v;
		bool f = false;
		memset(head,-1,sizeof(head));
		for(i=0;i<m;i++)
		{
			scanf("%d%d",&u,&v);
			if(u>v)
			{
				swap(u,v);
			}
			add_edge(u,v);
			add_edge(v,u);
			if(u==1 && v==n)
			{
				f = true;
			}
		}
		
		num = 0;
		
		if(f)
		{
			printf("%lld\n",bfs());	
		}
		else
		{
			printf("%lld\n",spfa());
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值