您是一位在偏远岛屿的邮局工作的程序员。您居住的区域包括几个岛屿。每个岛屿都有一个或多个港口城镇。除了它们之外,可能还有其他城镇。您必须乘船从一个岛到另一个岛。您可以使用陆地路线环岛,但有时使用海上路线会更快。 随着近年来邮局的私有化,全国各地的邮递员数量已经减少,以降低成本。偏远岛屿上的邮局也不例外,因此,Rito先生是唯一的邮递员。由于邮局负责收集和传递的区域很大,因此仅收集和传递是一项艰巨的任务。因此,Rito先生已向您寻求帮助,以帮助您高效地进行收集和交付。 您的任务是编写一个程序,寻找最短的路线,这取决于Rito先生要遵循的城镇接送顺序。 Rito先生永远不能按指定顺序以外的任何顺序执行收集和交付工作。但是,当您从一个城镇或村庄转移到另一个城镇或村庄时,您可以穿越另一个城镇或村庄。此外,Rito先生还有一艘船可以绕过岛屿。 例如,给定A镇,B镇和C村的收货和交货顺序,您可以通过任何一个镇或村从A镇到B镇。此时,您可能会经过C村,但是为了保持收货和送货的顺序,您必须一次到B镇执行收货和送货,然后再次访问C村并进行收货和送货。如果您通过海路从镇A到镇B,并且通过陆路从镇B到镇C,则船将留在镇B。因此,如果您想使用下一条航海路线,则需要返回到B镇。 可能需要在一个城镇或村庄多次收集和运送。例如,可以给出城镇A,乡村B,城镇C和乡村B的收货和交货顺序。目前,如果您没有跟随B村就从A镇前往C镇,您将无法立即在C镇提货。这是因为第一村B中的收集和交付尚未完成。在C镇收集并交付后访问B村,尚未结束B村的第一次收集和交付。 里托先生总是在港口镇住船。由于Rito先生是一位资深人士,因此他可以无视旅行时间以外的收集和交付工作所需的时间。而且,仅是在最后一个城镇或村庄完成收集和交付工作的时间是一个问题,并且不必考虑将船返回其原始位置并返回邮局的时间。
Input
输入包含多个数据集。每个数据集的格式如下
N M
x1 y1 t1 sl1
x2 y2 t2 sl2
...
xM yM tM slM
R
z1 z2 ... zR
数据集中的所有条目均为非负整数。一行中的输入项用一个空格分隔。 第一行指定陆地和海洋网络的规模。 N(2≤N≤200)是城镇或村庄的数量。每个城镇或村庄都分配一个从1到N的唯一编号。 M(1≤M≤10000)是陆地和海上航线的总数。 第2到1 + M行是陆路或海路的描述。 xi和yi(1≤xi,yi≤N)代表两端的城镇或村庄数。 ti(1≤ti≤1000)表示陆地或海上航线的旅行时间。 sli是“ L”或“ S”,其中L代表土地,S代表海洋。 直接连接两个城镇或村庄的陆路或海上路线可能不止一个。每个陆地或海洋路线都是双向的,也就是说,它可以沿任一方向移动。 M + 2行中的R(1≤R≤1000)指示Rito先生处理的收件和送达目的地的数量。在M + 3行上,按照收集和交付的顺序排列了目的地城镇zi的R数zi(1≤zi≤N)。 在初始状态下,Rito先生和这艘船都位于港口城镇z1。您始终可以通过某种方式从初始状态移至目标城镇或村庄。 输入的结尾由包含两个零的行表示,两个零之间用空格隔开。
Output
对于每个输入数据集,找到Rito先生按照给定的收货和交货顺序在城镇中巡查所需的最短旅行时间,然后将其输出到一行。
Sample Input
3 3 1 2 5 L 1 2 7 S 2 3 11 S 3 1 2 3 5 5 1 2 15 L 2 3 10 L 4 5 7 L 1 3 30 S 3 4 100 S 5 1 3 5 4 1 0 0
Output for the Sample Input
18 269
这题其实主要考察的是dp,由于数据量小(N最大200),O(V3)都可以(毕竟8s)。
对于两点之间的最短距离,我们可以用Floyd算法进行处理,由于海陆两种方式可以混着选,所以我们不妨开两个数组min_land,min_sea记录最小v到u的陆地距离和海洋距离。
至于接下来怎么求,我们可以想到用动态规划的方法(ord[i]为第i个目标村子的编号,因为对于要到达的序列R,ord[i]的就是R中第i个的值):
我们不管过程,只管状态,我们知道到达下一个顶点肯定要从上一个顶点出发,距离是到达上一个顶点的距离加上上一个到达下一个顶点的距离,但是由于船的位置不确定,而且下一次船停靠的位置也不确定,我们不如采用二维数组来记录每次的状态,我们用dp[i][j]表明现在到达第i个目标村(不是编号为i的村子,而是表示到达ord序列的第i个了),此时船停留在第j号村内,那么dp[i][j] = 对于dp[i-1][k]的每一种情况(因为上一次船可能停在任何地方,我们假设停留在k村子内),值为dp[i - 1][k] + 从第i-1号村到达第k号村去取船的时间min_land[ord[i - 1]][k]和将它开到第j号村的时间min_sea[k][j],最后在从j到达第i号村min_land[j][ord[i]]。这里为什么不是先到i号村再送船,如果这样我们仍然还要从j回到i,这样其实是从i-1到i加上i到k,加上k到j,加上j到i,我们知道从i-1到k的陆地最短时间一定小于等于i-1到i,i再到k的时间(不然i-1到k的陆地时间就要更新),所以不能先到i。
至于为什么从j到i要走陆地,因为我们dp[i][j]就是要满足到达第i个目标村子且船停留在j,如果在别的地方停了船,走陆地,那么船就不会停留在j点,特别注意,我们只管状态。
还要要注意的就是当j==k时,船是不要移动的,我们不需要回到k去移动船,所以这种情况的状态转移方程和其他的不一样直接就是dp[i-1][k] + min_land[ord[i - 1]][ord[i]](注:此时i==k)。
那么我们就得到了一下状态转移方程式:
如果j != k: dp[i][j] = min(dp[i - 1][k] + min_land[ord[i - 1]][k] + min_sea[k][j] + min_land[j][i], dp[i][j]);
如果j == k: dp[i][j] = min(dp[i - 1][k] + min_land[ord[i - 1]][ord[i]], dp[i][j]);
AC代码(参考https://www.cnblogs.com/ibilllee/p/9239869.html):
#include#include #include using namespace std; //最后的答案int放不下,要用longlong const long long INF = 0x1f3f3f3f3f3f3f3f;//注意这里4*INF不能超过longlong的范围 long long dp[1005][205];//dp[i][j]:到达第i个目标(注意村子编号不等于i,要看后面R的序列情况)村子船在j号村子这种情况下花费的最小时间 long long min_land[1005][1005];//v到u走陆地的最短时间 long long min_sea[1005][1005];//v到u走海洋的最短时间 int ord[1005];//R后面的序列,即第i个目标对于的村子编号 char temp[10];//读取是L还是S int main(void) { int n, m, r; int s, e, t; while(scanf("%d %d", &n, &m) && n + m) { long long ans = INF; fill(min_land[0] , min_land[0] + 1005 * 1005, INF); fill(min_sea[0] , min_sea[0] + 1005 * 1005, INF); fill(dp[0] , dp[0] + 1005 * 205, INF); for(int i = 1; i <= n; i++)//Floyd的初始化min_[i][i] = 0,其他为INF { min_land[i][i] = min_sea[i][i] = 0; } for(int i = 0; i < m; i++) { scanf("%d %d %d %s", &s, &e, &t, temp); if(temp[0] == 'L') { min_land[s][e] = t; min_land[e][s] = t; } else { min_sea[s][e] = t; min_sea[e][s] = t; } } //Floyd计算 for(int k = 1; k <= n; k++) for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { min_land[i][j] = min(min_land[i][j], min_land[i][k] + min_land[k][j]); min_sea[i][j] = min(min_sea[i][j], min_sea[i][k] + min_sea[k][j]); } scanf("%d", &r); for(int i = 1; i <= r; i++) scanf("%d", &ord[i]); dp[1][ord[1]] = 0;//第一个节点时必在1号,船也是,其他情况都不可达 for(int i = 2; i <= r; i++)//到达下一个目标 for(int j = 1; j <= n; j++)//对于每一种船停留的状态 for(int k = 1; k <= n; k++)//对于上一个节点时船停留的状态(因为上一个节点船也可以从1到n进行停泊,所以所有的情况都要考虑 { if(k != j) dp[i][j] = min(dp[i][j], dp[i - 1][k] + min_land[ord[i - 1]][k] + min_sea[k][j] + min_land[j][ord[i]]); else dp[i][j] = min(dp[i][j], dp[i - 1][j] + min_land[ord[i - 1]][ord[i]]); } for(int i = 1; i <= n; i++)//当dp[r][]时,也就是到达了最后一个目标村子后,船可能停泊在1到n村子里,这些都满足条件,我们要找到最小的解 ans = min(ans, dp[r][i]); printf("%lld\n", ans); } return 0; }