Damaged Bicycle(2021 CCPC 哈尔滨站)
G. Damaged Bicycle
time limit per test
3 seconds
memory limit per test
512 megabytes
Ring Ring Ring … The bell rang at half past six in the morning. After turning off the alarm, George went on to sleep again. When he woke up again, it was seven fifty and there were ten minutes left for class!
George immediately got up from bed, dressed, packed his backpack, brushed his teeth and washed his face. Then, he immediately rushed out of the dormitory and embarked on the road to the teaching building. On the way, he turned on his mobile phone to locate and saw several yellow shared bicycles nearby. Therefore, he headed to a bicycle and took out his mobile phone to scan the QR code on the bicycle. Unfortunately, the bicycle wasn’t unlocked, and a line of words “this bicycle is damaged and can’t be unlocked” was displayed on the screen.
Without riding a bicycle, he was late. What a bad day!
Indeed, some bicycles in the school are damaged, but their location will still be displayed on the app. George always rides faster than he walks, but considering that some bicycles are damaged, if George tries one by one, it may take a long time! In this regard, he has made some modeling, and hopes you can help him find the best strategy.
The campus can be modeled as a graph of nn vertices and mm bidirected edges, where the ii-th edge is wiwi meters long. George’s dormitory is located at vertex 11 and Guanghua Tower (the teaching building) is at vertex nn, so George has to go from vertex 11 to vertex nn to take classes. His walking speed is tt meters per second and his riding speed is rr meters per second. According to the bicycle sharing app, there are kk parked bicycles in the campus. The ii-th bicycle is located at vertex aiai, and of probability pi100pi100 to be damaged according to George’s experience. However, only when George arrives at vertex aiai and scans the QR code, can he determine whether the ii-th bicycle is damaged or not. As soon as a bicycle is confirmed to be undamaged, George will get on it immediately and will not get off until he reaches vertex nn.
Now George wants to save time to get to the classroom. So you, George’s roommate, should help him find an optimal strategy to minimize the mathematical expectation of the time cost on the way, and then output this value. Or you can let him continue sleeping if vertex nn is not reachable.
In this problem, you should only consider the time of walking and cycling, and you can assume that the other actions(scanning QR code, getting on, getting off, ⋯⋯) cost no time.
Input
The first line contains two integers t,r(1≤t≤r≤104)t,r(1≤t≤r≤104) — the speed of walking and the speed of riding, respectively.
The second line contains two integers n,m(1≤n,m≤105)n,m(1≤n,m≤105) — the number of vertices and the number of bidirected edges in the given graph.
Following mm lines each contains three integers ui,vi,wi(1≤ui,vi≤n,ui≠vi,1≤wi≤104)ui,vi,wi(1≤ui,vi≤n,ui≠vi,1≤wi≤104), denoting that vertices u,vu,v are connected by a wiwi-meter-long bidirected edge.
The next line contains a single integer k(0≤k≤18)k(0≤k≤18), denoting the number of bicycles in campus.
Following kk lines each contains two integers ai,pi(1≤ai≤n,0≤pi≤100)ai,pi(1≤ai≤n,0≤pi≤100), denoting the locations of the bicycles and the percentages of damage probabilities respectively.
It is guaranteed that no two bicycles are in the same vertex.
Output
If George cannot reach vertex nn, output one line containing one integer “-1”, or output one line containing one real number, denoting the minimum expectation of the time cost on the way.
As long as the relative or absolute error between your answer and the standard answer is within 10−610−6, your answer will be considered correct.
Examples
Input
Copy
3 15
4 3
1 2 600
1 3 300
2 4 900
1
3 50
Output
Copy
460.000000
Input
Copy
3 15
5 4
1 2 600
1 3 300
2 5 900
3 4 3
2
3 50
4 0
Output
Copy
220.600000
Input
Copy
3 15
5 4
1 2 600
1 3 300
4 5 900
3 2 300
2
3 50
4 0
Output
Copy
-1
Note
For the first test case, one possible strategy is:
- Go along the route 1→31→3 and try to ride the only bicycle in the campus.
- If the bicycle is damaged, go along the route 3→1→2→43→1→2→4 on foot, or go by bicycle.
Considering the time cost on the way:
- If the bicycle is damaged, George should go along the route 1→3→1→2→41→3→1→2→4 on foot, whose total length is 2100 meters. So the time cost is 21003=70021003=700 seconds.
- If the bicycle is undamaged, George should go along the route 1→31→3 on foot, whose total length is 300 meters, and then go along the route 3→1→2→43→1→2→4 by bicycle, whose total length is 1800 meters. So the time cost is 3003+180015=2203003+180015=220 seconds.
As given in the input, the only bicycle has 5010050100 probability to be damaged. So the expectation time cost is 50100×700+(1−50100)×220=46050100×700+(1−50100)×220=460.
解题思路
这道题目是一个图论问题,结合了概率与动态规划的思想。乔治需要从宿舍(顶点1)出发,去往教学楼(顶点n),路上可以尝试骑共享单车。如果单车坏了,他就继续步行。我们的目标是最小化乔治到达教学楼所需的期望时间。
题目给出了校园图,若干顶点之间有边相连,并且有共享单车停放在某些顶点。每辆单车有一定概率是坏的,乔治只有到达单车点时才能知道单车是否损坏。为了解决这个问题,我们可以采用**状态压缩动态规划(状压DP)**来计算最小的期望时间。
思路解析
1. dij
首先,对于图中每个顶点之间的最短路,我们可以使用Dijkstra算法进行计算:
- 计算从顶点1(宿舍)到其他所有顶点的步行最短路。
- 计算从顶点n(教学楼)出发到其他所有顶点的骑车最短路(逆向求解)。
2. 状态压缩动态规划
单车的数量最多为18辆,因此可以使用状态压缩的方法来表示哪些单车已经被访问过。我们用一个二进制数的每一位表示一辆单车是否已经被访问。
设:
Pro[S]
表示在状态S下所有单车都是坏的的概率。Dp[S][i]
表示访问过状态S中的所有单车,最后停在顶点i时的最小期望时间。
转移过程中,我们需要考虑以下三种情况:
- 在访问单车i之前已经找到了好单车:在这种情况下,后面的单车不再影响乔治的时间。
- i是第一辆好单车:乔治可以骑这辆单车后直接到教学楼,期望时间会缩短。
- i是坏的:继续访问下一个单车,期望时间会增加。
3. DP
对每个状态S
,我们枚举从状态S
转移到下一个状态S'
时的最优策略。在状态S
中,我们可以从已经访问过的单车中选择一个作为“最后”访问的单车点,然后基于该状态和概率,更新DP表。
4. 结果计算
最终的答案是: min(Dp[S][i]+Pro[S]×Dist(i,n)t)\text{min}(Dp[S][i] + Pro[S] \times \frac{Dist(i,n)}{t})min(Dp[S][i]+Pro[S]×tDist(i,n)) 即为最优路径的期望时间。
实现步骤
-
输入处理:读取步行速度、骑行速度、顶点数、边数等基本信息。
-
Dijkstra算法
:
- 计算步行从宿舍到其他所有顶点的最短路。
- 计算骑行从教学楼到其他所有顶点的最短路。
-
初始化DP状态:初始化DP表
Dp
和Pro
。 -
状态转移:通过状态压缩,枚举每种状态下的最小期望时间,并且进行DP转移。
-
输出答案:输出最小期望时间,如果教学楼不可达则输出-1。
dfs
#include <map>
#include <set>
#include <fstream>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <bitset>
#include <iomanip>
#define endl '\n'
#define int long long
#define Max(a, b) (((a) > (b)) ? (a) : (b))
#define Min(a, b) (((a) < (b)) ? (a) : (b))
#define BoBoowen ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
using namespace std;
const int N = 1e5 + 5;
double dp[19][1 << 18];
int hard[N];
double lin[19][N];
int ss[19][N];
int bj[20], bj1[N];
int nn;
int d[100];
double w1[20];
struct duqi
{
int i, end;
double k;
} q[N * 2];
struct du
{
double lenth;
int i;
bool operator<(const du &w1) const
{
return lenth > w1.lenth;
}
} t1, t2;
priority_queue<du> w;
void zdl(int n)
{
for (int i = 0; i <= nn; i++)
{
for (int j = 1; j <= n; j++)
{
lin[i][j] = 1e12;
}
}
for (int i = 0; i <= nn; i++)
{
t1.i = bj[i];
t1.lenth = 0;
lin[i][t1.i] = 0;
w.push(t1);
while (!w.empty())
{
t2 = w.top();
w.pop();
if (ss[i][t2.i])
{
continue;
}
ss[i][t2.i] = 1;
for (int j = hard[t2.i]; j != 0; j = q[j].end)
{
int l = q[j].i;
if (lin[i][l] > q[j].k + lin[i][t2.i])
{
lin[i][l] = q[j].k + lin[i][t2.i];
t1.i = l;
t1.lenth = lin[i][l];
w.push(t1);
}
}
}
}
}
double gl[1 << 18];
signed main()
{
BoBoowen;
int l, n, m, p = 0;
double v1, v2, lenth, h, min1;
cin >> v1 >> v2;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int r;
cin >> l >> r >> lenth;
q[++p].i = r;
q[p].end = hard[l];
q[p].k = lenth;
hard[l] = p;
q[++p].i = l;
q[p].end = hard[r];
q[p].k = lenth;
hard[r] = p;
}
cin >> nn;
bj[0] = 1;
for (int i = 1; i <= nn; i++)
{
int k;
double x;
cin >> k >> x;
bj[i] = k;
bj1[k] = i;
w1[i] = x;
}
zdl(n);
if (lin[0][n] >= 1e10)
{
printf("-1");
return 0;
}
d[0] = 1;
for (int i = 1; i <= 20; i++)
d[i] = d[i - 1] * 2;
for (int i = 0; i <= d[nn] - 1; i++)
{
h = 1;
for (int j = 0; j < nn; j++)
{
if ((1 << j) & i)
{
h = h * (w1[j + 1]) / 100;
}
}
gl[i] = h;
}
min1 = 1e12;
for (int j = 1; j <= nn; j++)
{
dp[j][d[j - 1]] = lin[0][bj[j]] / v1 + lin[j][n] * (1 - w1[j] / 100) / v2;
min1 = min(min1, dp[j][d[j - 1]] + gl[d[j - 1]] * lin[j][n] / v1);
}
int g = 0;
for (int i = 1; i <= d[nn] - 1; i++)
{
if (i == d[g])
{
g++;
continue;
}
for (int j = 1; j <= nn; j++)
{
dp[j][i] = 1e12;
if ((1 << (j - 1)) & i)
{
for (l = 1; l <= nn; l++)
{
if ((1 << (l - 1)) & i && l != j)
{
dp[j][i] = min(dp[j][i], dp[l][i - d[j - 1]] + lin[l][bj[j]] * gl[i - d[j - 1]] / v1 + gl[i - d[j - 1]] * lin[j][n] * (1 - w1[j] / 100) / v2);
}
}
}
min1 = min(min1, dp[j][i] + gl[i] * lin[j][n] / v1);
}
}
cout << fixed << setprecision(6) << min(min1, lin[0][n] / v1);
}