Waterloo Local Contest Bridges and Tunnels 最短路(dijkstra 邻接表+优先队列)

首先放上题目

Problem A: Bridges and Tunnels

It may feel warm now, but in a few months, Waterloo will be full of snow. Luckily, many of the buildings on campus are connected by bridges and tunnels, so you do not need to go outside very much. The network of buildings can be confusing, and it is hard to know the best way to get from one building to another. A computer program could help.

Input Specification

The first line of input contains three integers 0 < n <= 40000 < m <= 400000 < p <= 30, the number of buildings on campus, the number of (indoor or outdoor) paths between the buildings, and the number of trips that you would like to make. Buildings are numbered sequentially from 0 to n-1. Each of the next m lines describes a path between buildings with three integers and a letter. The first two integers specify the two buildings connected by the path. The path can be taken in either direction. The third integer specifies the number of seconds required to take the path from one building to the other. The number of seconds is at least 0 and at most one million. Finally, the letter is I if the path is indoors, or O if the path is outdoors. Each of the next p lines describes a trip from one building to another using two integers, the numbers of the two buildings.

Sample Input

2 1 1
0 1 30 I
0 1

Output Specification

For each trip, find the optimal route between the specified two buildings. The optimal route minimizes the amount of time spent outside. Among routes that require spending the same amount of time outside, the optimal route minimizes the total time spent. For each trip, output a single line containing two integers, the time spent outside and the total time spent on the optimal route. If there is no route connecting the two specified buildings, output instead a line containing the word IMPOSSIBLE.

Output for Sample Input

0 30

【题意】不久后滑铁卢将会变得非常冷,但是幸运的是,很多建筑都被桥梁和隧道连接着,所以你不需要总是走在外面。但是现在建筑物之间的连接是错综复杂的,很难知道某两个建筑物之间的最优路线,所以需要你写程序判断。给出 n 个点,m 条无向边,以及 p 个查询,边分为两种,一种是暴露在外面的边,用 O 表示,另一种是在室内的边,用 I 表示最优。

路线遵循以下规则:

1)尽可能使得路径上暴露在外面的边权值和最少;

2)在满足第一个条件的情况下,尽可能使得总路程最少。

每次查询给出一个 起点 s 和终点 t,求  s -> t 的最优路线。若路线存在则输出路径上暴露在外面的边的总和,以及路径的总和;否则输出

“IMPOSSIBLE”。

【分析】图的存储因为数据不是很大所以邻接表邻接矩阵都可以,在这里我们使用数组模拟的邻接矩阵。这是一道最短路的变式我们在更新路径时需要考虑边的状态即在室内还是室外。通过题目我们知道每次更新路径时先找到室外边最短的一条路。之后分成两种情况讨论

用outd[v] 代表 最短路到 v 点时室外边的最小和,sumd[v] 代表到 v 点时路径总和,u表示当前状态下的最短路

1)当前边属于室内边时

a) 若 outd[v] > outd[u],更新 outd[v] 以及 sumd[v],v 入队;

b) 若 outd[v] == outd[u] && sumd[v] > sumd[u] + dis[u, v],更新 outs[v] 以及 sumd[v],v 入队;

2)当前边属于室外边时

c) 若outd[v] > outd[u] + dis[u, v],更新 outd[v] 以及 sumd[v],v 入队;

d) 若 outd[v] == outd[u] + dis[u, v] && sumd[v] > sumd[u] + dis[u, v],更新 outd[v] 以及 sumd[v],v

入队。

然后给出代码

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

const long long INF=0x3f3f3f3f3f3f3f3f3f3f3f3f;

struct Edge{
    int v,next,pi;
    long long dis;
};

struct P{
    int v;
    long long outdis;
    long long sumdis;
    P(){}
    P(int _v,long long _outdis,long long _sumdis):v(_v),outdis(_outdis),sumdis(_sumdis){}
    friend bool operator<(P a,P b){
        if(a.outdis==b.outdis)
            return a.sumdis>b.sumdis;
        return a.outdis>b.outdis;
    }
};

int n,m,p;

int first[4005],num;
Edge graph[80005];
int a,b;
long long d;
char str[3];
long long outd[4005];
long long sumd[4005];

void addEdge(int u, int v,long long dis,int pi){
	graph[num].v = v;
	graph[num].dis = dis;
	graph[num].next = first[u];
	graph[num].pi = pi;
	first[u] = num++;
}

void dijkstra(int s)
{
	fill(outd,outd+n,INF);
	fill(sumd,sumd+n,INF);
	priority_queue <P> q;
	while(!q.empty())
        q.pop();
	outd[s]=sumd[s]=0;
	q.push(P(s,0,0));
	while(!q.empty()){
		P p=q.top();
        q.pop();
		int u=p.v;
		for(int i=first[u];i!=-1;i=graph[i].next){
			int v=graph[i].v;
			if(graph[i].pi==0){
				if(outd[v]>outd[u]){
					outd[v]=outd[u];
					sumd[v]=sumd[u]+graph[i].dis;
					q.push(P(v,outd[v],sumd[v]));
				}
				else if(outd[v]==outd[u]&&sumd[v]>sumd[u]+graph[i].dis){
					outd[v]=outd[u];
					sumd[v]=sumd[u]+graph[i].dis;
					q.push(P(v,outd[v],sumd[v]));
				}
			}
			else{
				if(outd[v]>outd[u]+graph[i].dis){
					outd[v]=outd[u]+graph[i].dis;
					sumd[v]=sumd[u]+graph[i].dis;
					q.push(P(v, outd[v], sumd[v]));
				}
				else if(outd[v]==outd[u]+graph[i].dis&&sumd[v]>sumd[u]+graph[i].dis){
					outd[v]=outd[u]+graph[i].dis;
					sumd[v]=sumd[u]+graph[i].dis;
					q.push(P(v, outd[v], sumd[v]));
				}
			}
		}
	}
}

int main()
{
    num = 0;
	memset(first, -1, sizeof(first));
	scanf("%d%d%d", &n, &m, &p);
	for(int i=0;i<m;i++){
		scanf("%d%d%ld%s",&a,&b,&d,str);
		if(str[0]=='I'){
			addEdge(a, b, d, 0);
			addEdge(b, a, d, 0);
		}
		else{
			addEdge(a, b, d, 1);
			addEdge(b, a, d, 1);
		}
	}
	for(int i=1;i<=p;i++){
		scanf("%d%d",&a,&b);
		dijkstra(a);
		if(sumd[b]==INF)
            printf("IMPOSSIBLE\n");
		else
            printf("%lld %lld\n", outd[b], sumd[b]);
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值