OJ链接:https://vjudge.net/problem/UVA-1025
题意:
一段笔直的双向铁路上有N个火车站,首站和尾站均有火车发出,首站发出的火车经过中间的所有车站后到达尾站,反之同理,列车经过中间站时有短暂的停车时间(忽略该时间),特工可在列车靠站时上下车,现规定特工到达首站时为时刻 0 时,在给定的时刻T 时恰好位于尾站,并且使得特工在车站的等车时间尽可能少。
输入给出:车站总数N,时刻T,火车通过相邻车站所需的时间Ti ,首站发出的火车数M1,每辆车出发的时刻T1i,尾站发出的火车数M2,每辆车出发的时刻T2i。
输出给出:能够在时刻T准时到达尾站时,最少的等车时间;不能在时刻T准时到达时候,则输出impossible。
解析:
该题目要求特工尽可能的待在火车上,但首先要确保在时刻T能在出现在尾站。可能的情况是:时刻T恰好在尾站下车,或者在尾站等到时刻T,或者无法及时到尾站。
前两者情况均有最小等车时间,最后一种情况无最小等车时间。
算法:
暴力算法:以时刻为循环单位,遍历0到T的所有时刻,在每个时刻,首先判断特工是否到达车站,当特工到达车站时,可以有3个选择,原地等车;乘坐向尾站开的火车(如果有);乘坐向首站开的火车(如果有)。
每次记录下当前时刻所在的车站和已等车的时间。最终在t=T时结束循环,找到车站为尾站且时间为T的所有结点,取时间最小的那个,即为答案。
可以看出,这是一颗三叉树,如果输入火车通过每个相邻车站时间均为1时(即每一时刻均会到达车站),则该树在最坏的情况下总共有(3的T次方-1)/2个结点,T取200时,大约为1.3×10的95次方,无论用BFS还是DFS遍历,均会超时。
动态规划:前面的暴力算法中,已知到达车站时有3种选择,并且,每个时刻是不同的状态。则可以规定数组DP[t][n]。其中,t表示当前时刻,n表示当前所在车站序号(首站为1,尾站为N),数组内存储的是当前(t,n)下已经等车的最小时间。
状态转移方程:dp[t][n] = min( dp[t-1][n]+1 , dp[t-T1i][n-1] , dp[t-T2i][n+1] )
分别对应了3种选择,其中n不变,t-1时,则说明上一时刻选择了在原车站等待,则累计等待时间+1;第二种,n-1,说明从前一车站乘车到达该车站,说明在前一车站的某个时刻选择了乘坐开往尾站的火车,则累计等待时间继承自前一车站,并且那个时刻为t-T1i(T1i为火车通过相邻车站花费的时间),最后一种同理。
最后输出dp[T][N]即为答案(表示时刻T时在车站N已经等待的最小时间),注意,若dp[T][N]大于T,说明到达N站(尾站)时的最小等车时间大于要求到达的时刻T,则输出impossible。
剩下最后一个问题就是判断在每个时刻当前所在的车站是否有车到达(有车到达说明可以乘坐该车),通过创建数组hastrain[t][n][2]来记录,其中第hastrain[t][n][0]表示首到尾,[1]表示尾到首,这个判断可以在输入时直接由发车时间和路上花费的时间来计算。
代码:
#include <iostream>
#include <string.h>
using namespace std;
int dp[202][52], hastrain[202][52][2], tim[52], depart, m1, m2