灾后重建(洛谷P1119)

问题描述

B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。

给出 B 地区的村庄数 N,村庄编号从 0 到 N - 1,和所有 M 条公路的长度,公路是双向的。并给出第 i 个村庄重建完成的时间 ti ,你可以认为是同时开始重建并在第 ti 天重建完成,且在当天即可通车。若 ti 为 0 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 Q 个询问 ( x , y , t ) ,对于每个询问你要回答在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少。如果无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x 或村庄 y 在第 t 天仍未重建完成,则需要返回 -1 。

输入格式

第一行包含两个正整数 N , M , 表示了村庄的数目与公路的数量。

第二行包含 N 个非负整数 t0,t1,… ,t(N-1),表示了每个村庄重建完成的时间,数据保证了 t0 ≤ t1 ≤ … ≤ t(N-1) 。

接下来 M 行,每行 3 个非负整数 i , j , w,w 为不超过 10000 的正整数,表示了有一条连接村庄 i 与村庄 j 的道路,长度为w,保证 i ≠ j,且对于任意一对村庄只会存在一 条道路。

接下来一行也就是 M + 3 行包含一个正整数 Q,表示Q个询问。

接下来 Q 行,每行 3 个非负整数 x , y , t,询问在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少,数据保证了 t 是不下降的。

输出格式

共 Q 行,对每一个询问 ( x , y , t ) 输出对应的答案,即在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少。如果在第 t 天无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x 或村庄 y 在第 t 天仍未修复完成,则输出−1。

输入样例
4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4
输出样例
-1
-1
5
4
说明/提示

对于 30% 的数据,有 N ≤ 50 ;

对于 30% 的数据,有 ti = 0,其中有 20% 的数据有 ti = 0 且 N > 50;

对于 50% 的数据,有 Q ≤ 100 ;

对于 100% 的数据,有 N ≤ 200,M ≤ N * (N-1)/ 2 ,Q ≤ 50000,所有输入数据涉及整数均不超过 100000。

算法思路

题目本质上就是求多源点最短路问题,由于数据较小,所以可以用Floyd算法。

又因为每个村庄的重建时间是升序的,并且每个询问中的 t 也是不下降的,所以可以用在线解法。

每输入一次询问,就将重建时间小于 t 的村庄加入到规划中,更新dist矩阵。保存此次询问的答案。

最后将每次询问保存的答案ans数组输出。

实现代码C++
// 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// 洛谷P1119 - 灾后重建

#include <iostream>
using namespace std;

const int MAX_N = 200;
const int MAX_M = MAX_N * (MAX_N - 1) / 2;
const int MAX_DIST = 100001;
const int MAX_Q = 50000;

void BuildMinDist(int dist[MAX_N][MAX_N], int N, int k)
{
	int i, j;

	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			if (dist[i][k] + dist[k][j] < dist[i][j])
			{
				dist[i][j] = dist[i][k] + dist[k][j];
			}
		}
	}
}

int getAns(int dist[MAX_N][MAX_N], int RebuildDates[], int x, int y, int t)
{
	int ans = -1;

	if (RebuildDates[x] > t || RebuildDates[y] > t || dist[x][y] == MAX_DIST)
	{
		ans = -1;
	}
	else
	{
		ans = dist[x][y];
	}

	return ans;
}

// 输出dist矩阵,调试使用。
void printdist(int dist[][MAX_N],int N)
{
	cout << "---start-------------" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cout << dist[i][j] << "\t\t";
		}
		cout << endl;
	}
	cout << "---end-------------" << endl;
}

int main()
{
	int N;			// 村庄数
	int M;			// 公路数
	int i, j, w;	// 连接村庄i和村庄j的道路长度为w
	int Q;			// 询问数
	int x, y, t;	// 在第t天,从村庄x到村庄y
	
	int dist[MAX_N][MAX_N];		// 村庄之间的距离矩阵
	int RebuildDates[MAX_N];	// 村庄的重建时间
	int ans[MAX_Q];

	// 输入村庄数、公路数
	cin >> N >> M;

	// 初始化dist矩阵
	for (int a = 0; a < N; a++)
	{
		for (int b = 0; b < N; b++)
		{
			if (a == b)
			{
				dist[a][b] = 0;
			}
			else
			{
				dist[a][b] = MAX_DIST;
			}
		}
	}

	// 输入每个村庄的重建时间
	for (int a = 0; a < N; a++)
	{
		cin >> RebuildDates[a];
	}

	// 输入每条公路的长度
	for (int a = 0; a < M; a++)
	{
		cin >> i >> j >> w;
		dist[i][j] = w;
		dist[j][i] = w;
	}

	// 输入询问数
	cin >> Q;

	// 输入Q组询问并保存每组询问的答案。k代表待重建的村庄编号。
	int k = 0;	
	for (int a = 0; a < Q; a++)
	{
		cin >> x >> y >> t;
		while (RebuildDates[k] <= t && k < N)
		{
			// 在线构建最短距离矩阵
			BuildMinDist(dist, N, k);
			k++;
		}
		ans[a] = getAns(dist, RebuildDates, x, y, t);
	}

	// 输出答案
	for (int a = 0; a < Q; a++)
	{
		cout << ans[a] << endl;
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值