【Dijkstra】CCF201712-4 行车路线
问题描述
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
输入格式
输入的第一行包含两个整数n, m,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
输出格式
输出一个整数,表示最优路线下小明的疲劳度。
样例输入
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
样例输出
76
样例说明
从1走小道到2,再走小道到3,疲劳度为52=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
对于30%的评测用例,1 ≤ n ≤ 8,1 ≤ m ≤ 10;
对于另外20%的评测用例,不存在小道;
对于另外20%的评测用例,所有的小道不相交;
对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保证答案不超过106。
对于另外20%的评测用例,不存在小道;
对于另外20%的评测用例,所有的小道不相交;
对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保证答案不超过106。
分析
用Dijkstra算法求最短路径问题。但是题目中多的一个条件是分为大路和小路两种,所以用两个数组sr[],br[]存储从v0到vi的当前最短路径,意思是如果当前这条路是小路,就将结果存在sr[]中,如果是大路就存在br[]中。
按课本上的Dijkstra算法修改一下使用。
1 #include <iostream> 2 #include <iomanip> 3 #include <sstream> 4 #include <cstdio> 5 #include <string.h> 6 #include <cstring> 7 #include <algorithm> 8 #include <cmath> 9 #include <string> 10 #include <queue> 11 #include <map> 12 #include <vector> 13 #include <set> 14 #include <list> 15 using namespace std; 16 typedef long long ll; 17 const int INF = 0x3f3f3f3f; 18 const int NINF = 0xc0c0c0c0; 19 const int maxn = 505; 20 int MonthDay[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 21 ll big[maxn][maxn], small[maxn][maxn]; 22 int vis_big[maxn], vis_small[maxn]; 23 ll sr[maxn], br[maxn]; 24 ll path[maxn];//记录顶点v之前正在连续的小路 25 ll n, m; 26 ll Dijkstra(ll v0) 27 { 28 for(ll i = 0; i < n; i++) { 29 sr[i] = small[v0][i]; 30 br[i] = big[v0][i]; 31 if(sr[i] < INF) { 32 path[i] = sr[i]; 33 sr[i] *= sr[i]; 34 } 35 else path[i] = INF; 36 } 37 vis_small[v0] = vis_big[v0] = 1; 38 while(1){ 39 ll minn = INF, flag = 0, v = -1; 40 for(ll j = 0; j < n; j++) { 41 if(!vis_big[j] && br[j] < minn) { 42 flag = 0; 43 v = j; 44 minn = br[j]; 45 } 46 if(!vis_small[j] && sr[j] < minn) { 47 flag = 1; 48 v = j; 49 minn = sr[j]; 50 } 51 } 52 if(v == -1) break; 53 if(flag) vis_small[v] = 1; 54 else vis_big[v] = 1; 55 for(ll j = 0; j < n; j++) { 56 if(!vis_small[j] && small[v][j] < INF) { 57 if(flag) {//prev到v之间是small road 58 ll temp = sr[v] - path[v]*path[v] + (path[v]+small[v][j])*(path[v]+small[v][j]); 59 if(sr[j] > temp || sr[j] == temp && path[j] > path[v]+small[v][j]) { 60 sr[j] = temp; 61 path[j] = path[v]+small[v][j]; 62 } 63 } 64 else{//prev到v之间是big road 65 ll temp = br[v]+small[v][j]*small[v][j]; 66 if(sr[j] > temp || sr[j] == temp && path[j] > small[v][j]) { 67 sr[j] = temp; 68 path[j] = small[v][j]; 69 } 70 } 71 } 72 if(!vis_big[j] && big[v][j] < INF) { 73 if(flag) { 74 ll temp = sr[v] + big[v][j]; 75 br[j] = min(temp, br[j]); 76 } 77 else { 78 ll temp = br[v] + big[v][j]; 79 br[j] = min(temp, br[j]); 80 } 81 } 82 } 83 } 84 return min(br[n-1], sr[n-1]); 85 } 86 int main() { 87 88 ll t, a, b, c; 89 while(cin >> n >> m) { 90 for (ll i = 0; i < n; i++) { 91 for (ll j = 0; j <= i; j++) { 92 big[i][j] = big[j][i] = INF; 93 small[i][j] = small[j][i] = INF; 94 } 95 } 96 memset(vis_big, 0, sizeof(vis_big)); 97 memset(vis_small, 0, sizeof(vis_small)); 98 for (int i = 0; i < m; i++) { 99 cin >> t >> a >> b >> c; 100 a--; 101 b--; 102 if (t == 1) small[a][b] = small[b][a] = min(c, small[a][b]); 103 else big[a][b] = big[b][a] = min(c, big[a][b]); 104 } 105 cout << Dijkstra(0) << endl; 106 } 107 return 0; 108 }