洛谷 P1813 拯救小 tim 题解


一、题目内容

题目描述
小 tim 在游乐场,有一天终于逃了出来!但是不小心又被游乐场的工作人员发现了…所以你的任务是安全地把小 tim 护送回家。但是,A 市复杂的交通状况给你出了一大难题。

A 市一共有 n 个路口,m 条单行马路。但是,每条马路都只有一段时间是开放的。为了安全,你必须选择一条护送路线,使得小 tim 在路上的时间最短,即到家的时刻减去离开游乐场的时刻最短。

输入格式
第一行 44 个数,分别是 n,m,s,t。基地在路口 s,码头在路口 t。

接下来 mm 行每行 55 个数 x,y,b,e,c 表示一条 x 路口到 y 路口的单行线,在 b 时刻到 e 时刻之间开放,需要 c 的时间通过这条路(必须保证行进在路中间时,路一直开放,否则小 tim 会被捉住)。两个路口之间可能会有多条道路。一开始的时刻是 0(当然,你可以不用马上出发,在基地多呆一段时间)。

如果不存在任何一种方案使得小 tim 能成功到达码头,输出 Impossible。

输出格式
一行,为小 tim 在路上停留的最短时间。

输入输出样例
输入
4 5 1 4
1 2 0 1 1
1 2 0 1 2
1 3 1 3 2
2 4 3 4 1
3 4 3 4 1
输出
3

二、题目分析

题解基本都是dijkstra或者spfa而我还不想写而诞生的题解
观察数据后可以发现,这个题给出的时间最大值为10000,这个数值并不是很大,换言之,我们可以直接遍历时间,然后遍历每一个点,判断当前时间是否可以从某条边到达这个点,如果可以到达,就将这个时间点设置为到达该点的最小时间。然后一直重复这个操作直到到达t为止。
但是通过上面的操作我们只能得到到达的最早时间点t,而题目的要求是最短在路上停留的时间,所以我们还需要一个时间记录数组tim,专门用来记录i时间到达j点的最晚出发时间,显而易见,这个数字应该越大路上的时间就越短。
接下来就只需要一边遍历所有时间点,一边更新最晚的到达时间,最后将两个时间相减就是正确答案。
至于不能到达的情况,只需要将ans一开始设置为一个超过最大时间的数字,如果这个数字没有被更新的话,就说明无解。

这个题我一直在寻思能不能停在某一个点,结果是确实可以,但题目中并没有说,卡我了半天。


代码

写了四重循环,时间复杂度应该是O(T * m) 其中T为到达t的最终时间,m为边数

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <math.h>
#define ll long long

using namespace std;

struct line {
	ll x;
	ll y;
	ll b;
	ll e;
	ll c;
}p[1005];

ll n, m, s, t, tim[105][10005], ans = 10005, fin = 0;
vector<ll> gra[105], mir[105][105];
bool arr[105][10005];

int main() {
	cin >> n >> m >> s >> t;
	for (int i = 1; i <= m; i++) {
	//输入数据
		cin >> p[i].x >> p[i].y >> p[i].b >> p[i].e >> p[i].c;
		if (p[i].c <= p[i].e - p[i].b) {
			gra[p[i].y].push_back(p[i].x); //建图
			mir[p[i].x][p[i].y].push_back(i);//记录两点之间对应的边
		}
		fin = max(fin, p[i].e);
	}
	//这里的数组arr用来判断时间为i的时候是否能到达j
	for (int i = 0; i <= 10000; i++) {
		arr[s][i] = true;
		tim[s][i] = i;
	}
	for (int i = 1; i <= 10000; i++) {
		for (int j = 1; j <= n; j++) {
			if (j == s) continue;
			arr[j][i] = arr[j][i - 1];//数组arr用来判断时间为i的时候是否能到达j
			tim[j][i] = tim[j][i - 1];//数组tim用来记录i时间能到达j点的最晚出发时间
			for (int k = 0; k < gra[j].size(); k++) {
				int f = gra[j][k];
				for (int h = 0; h < mir[f][j].size(); h++) {
					int num = mir[f][j][h];//获取当前两个端点的对应边的序号
					ll b = p[num].b, e = p[num].e, c = p[num].c;
					//如果可以到达当前点j
					if (i <= e && i - c >= b) {
						arr[j][i] |= arr[f][i - c];
						tim[j][i] = max(tim[j][i], tim[f][i - c]);
					}
					//如果已经可以到达最终点t
					if (j == t && arr[j][i]) {
						ans = min(i - tim[j][i], ans);
					}
				}
			}
		}
	}
	if (ans == 10005) cout << "Impossible";
	else cout << ans;
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值