TCO
Problem Statement
Fox Ciel is going to walk through an unpaved road to meet her friend. The road is one-dimensional. It is separated into N parts numbered from 0 to N-1, where part 0 is Ciel's current part and part N-1 is her destination part.
Ciel will perform her trip tomorrow. Unfortunately today it is raining, so tomorrow some parts of the road will be muddy. You are given a int[] road containing N elements. The probability that the i-th part of the road will be muddy tomorrow is road[i]/100.
Ciel can walk along the road using any combination of strides with lengths 1 and 2. If she is at part i, a stride of length 1 will move her to part i+1 and a stride of length 2 will move her to part i+2 (skipping part i+1). If there are many ways to reach part N-1 from part 0, Ciel will choose the one among them where the number of visited muddy parts is minimal.
Return the expected number of muddy parts that she will visit tomorrow.
Definition
Class: MuddyRoad
Method: getExpectedValue
Parameters: int[]
Returns: double
Method signature: double getExpectedValue(int[] road)
(be sure your method is public)
Notes
- Assume that events "i-th part of the road will be muddy tomorrow" are totally independent.
- Ciel has very good sight, so when starting her trip at part 0, she is already able to see for each part whether it is muddy or not.
Constraints
- road will contain between 2 and 50 elements, inclusive.
- Each element of road will be between 0 and 100, inclusive.
- The first element and the last element of road will be 0.
Examples
0)
{0, 60, 60, 0}
Returns: 0.36
There can be four different states of the road tomorrow:
.... with probability = 0.16, 0 steps to muddy parts
.M.. with probability = 0.24, 0 steps to muddy parts
..M. with probability = 0.24, 0 steps to muddy parts
.MM. with probability = 0.36, 1 step to muddy parts
(Here, '.' represents a non-muddy part and 'M' represents a muddy part.)
Thus, the expected number of steps is 00.16+00.24+00.24+10.36=0.36.
1)
{0, 50, 50, 50, 50, 0}
Returns: 0.5625
Returns: 3.0
Returns: 1.7352539420031923
Returns: 2.288125
This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2010, TopCoder, Inc. All rights reserved.
This problem was used for:
2011 TCO Algorithm Online Round 1 - Division I, Level Two
Ciel knows in advance which parts are muddy and which parts are not. This means that whenever deciding to jump to the next or the second-next part she already knows the minimum number of muddy parts that she will have to go through if she picks each of them. The Fox may do a different strategy for each different composition of muddy parts. We are actually solving a problem on top of another problem. We need to find the strategy that the Fox will use given a muddy composition and then we need to calculate the expected number of muddy parts the Fox will visit given the probabilities that each part is muddy.
I will mention two approaches, one that is the most common but may end up being tricky to implement and a second one that is very easy to implement. I will not explain the first one in its entirely, just until we reach the complicated part. If you wish to skip to the explanation of the approach that will be explained entirely you can skip to the subtitle that reads "the greedy approach".
The first approach is to consider that the Fox will do a Dynamic programming-esque strategy : She will calculate a recurrence F(p) which returns the minimum number of muddy parts she will visit if she starts at part # p. We can assume that F(n-1) will be 0, because she has already reached the last location. Then F(n-2) will be equal to 1 if and only if part (n-2) is muddy. After that we can know that when solving F(x), Ciel will know F(x+1) and F(x+2), and then she will pick the one with the minimum such number - if F(x+1) is less than F(x+2) she will jump to part (x+1) - By using this approach, F(0) will give the minimum number of muddy parts. (Note that this is not the solution for the problem, but the solution that Ciel will use given a set of muddy parts.)
The idea is that once we know Ciel's approach we can try to calculate the expected number of muddy parts that she will visit. The discrete expected value asks us to, for each possible value, calculate its probability and then multiply them and add these products together to find the expected value. Thus we will need a new function G(x, cost) which will give us the probability that the minimum number of muddy parts that Ciel will visit when starting at part x will be cost, in other words G(x, cost) is the probability that [[F(x)=cost]]. Let us say that we decided to make G(x, cost) a recurrence. Then we will have two scenarios:
The part at x is muddy, this has probability p[x] (where p[x] is the probability that x will be muddy and equal to road[x]/100.0). Then the required cost for the next step is (rcost = cost-1).
The part at x is not muddy. Probability = 1.0 - p[x]. the required cost for the next step is (rcost = cost).
The initial idea is to make it a recurrence, we need to find the probability that the next picked part needs rcost muddy parts. Note that just knowing G(x+1, rcost) and G(x+2, rcost) is not enough. There are cases in which the cost F(x+1) will be equal to cost , but F(x+2) will not, yet we will still be able to pick F(x+1). How about we try all pairs (cost1, cost2) and find the probabilities: G(x+1, cost1) and G(x+2, cost2) ? Then given cost1 and cost2 we would know if the picked road will have cost equal to rcost (This happens when the minimum between cost1 and cost2 is equal to rcost). It is a sound idea, but here comes the catch: G(x+1, cost1) and G(x+2, cost2) are not independent events. The values F(x+1) and F(x+2) are interconnected, because it is possible to jump from F(x+1) to F(x+2). This means that, in order to calculate the probability for the event [ (muddy parts from x+1 is equal cost1) and (muddy parts from x+2 is equal cost2) ] we cannot just multiply G(x+1, cost1) * G(x+2, cost2).
The solution to that issue is not actually so hard to find. We may define a different recurrence H(x, cost1, cost2). The probability that [[ F(x) = cost1 AND F(x+1) = cost2) ]]. When you define this recurrence, it is possible to get G(x, cost), just sum together all values H(x, i) for each possible cost2 = i. This new recurrence is not very difficult to make but this approach will be left as an exercise.
The greedy approach
As we could see, it may at first seems easy to just think that the Ciel will use an approach that gives her all information about the parts by using dynamic programming, but at the end the implementation may be more complicated than it seems. The easier idea is to note that there is an optimal strategy that does not require information about all of the next muddy parts, but that it will also always minimize the number of visited muddy parts.
This time, we will try a different way to implement function F(x) - the minimum number of muddy parts visited if you start at part x. There are two possibilities about the next part (x+1): That it is muddy or that it is not muddy.
If (x+1) is not muddy. Then we must decide to jump to (x+1) or to (x+2). The important thing to notice here is that jumping to (x+1) will never be a bad decision. Simply because it will always be possible to jump from (x+1) to (x+2) if necessary. More formally, we can show that if (x+1) is not muddy then: F(x+1) <= F(x+2) - because we will always be able to move to (x+1) and then to (x+2) at no cost.
If part (x+1) is muddy. Then it makes sense intuitively to try to avoid it and jump directly to (x+2). This is actually always a good idea. In order to prove it, let us formalize things. We have:
F(x+1) = 1 + min(F(x+2), F(x+3) )
F(x+2) <= F(x+1)
F(x+2) <= 1 + min(F(x+2), F(x+3) )
i) F(x+2) <= 1 + F(x+2) //( if F(x+2) <= F(x+3) )
ii) F(x+2) <= 1 + F(x+3) //( if F(x+2) > F(x+3) )
i) is clearly true, as 1+F(x+2) is always larger than F(x+2). ii) is also true. It just needs us to notice that if F(x+2) is greater than F(x+3) and F(x+2) is <= 1 + F(x+3), then we can claim that:
if (F(x+2) > F(x+3)) ==> F(x+2) = F(x+3) + 1
Another interpretation is that for two consecutive parts, x and x+1, F(x) can exceed F(x+1) by at most 1 unit. This is true because if (x) was muddy then we can always go from (x) to (x+1) and the total cost would be (1 + F(x+1)). Therefore, the minimum cost from (x) will be at most (1 + F(x+1)).
Thus the optimal estrategy to find F(x) depends only on whether the next part (x+1) is muddy or not. If it is muddy then the fox must jump directly to part (x+2) and if it is not muddy, she should jump to part (x+1). This strategy will always yield an optimal result. What is important about this strategy is that it does not need prior knowledge of the results for F(x+1) and F(x+2), it only needs to know if the next part is muddy.
We can now procceed to solve the actual problem. Let us call E(x) the expected number of muddy parts we will visit if we start at part (x). There is a p[x] probability that part (x) is muddy, this raises the expected number by p[x]. Then there is a p[x+1] probability that the next cell is muddy.
If the next cell is muddy, then the result is E(x+2).
If the next cell is not muddy, then the result is N(x+1).
N(x) is a different function, because it returns the expected number of muddy parts to be visited when starting at x knowing that part (x) is not muddy. So we have:
E(x) = p[x] + p[x+1] * E(x + 2) + (1 - p[x+1]) * N(x + 1)
N(x) is evaluated the same way. Except that we know that (x) is not muddy.
N(x) = p[x+1] * E(x + 2) + (1 - p[x+1]) * N(x + 1)
Note that E(x) = N(x) + p[x]. So we can convert everything to a single recurrence:
N(x) = p[x+1] * (p[x+2] + N(x + 2)) + (1 - p[x+1]) * N(x + 1)
After calculating all values of N(x) we can find E(0) = p[0] + N(0). N(x) is a very simple recurrence. We can calculate it from top to bottom. Note that N(n-1) is equal to 0, because we assume that part (n-1) is not muddy and the path is finished once we reached part (n-1). N(n-2) is also known, the Fox will always jump directly to part (n-1), and the probability that (n-1) is muddy is p[n-1], so N(n-2) = p[n-1]. After that, we can calculate the values for all indexes i from n-3 to 0 with an iterative dynamic programming solution.
Summary
It pays to find a strategy for Ciel that is as simple as possible. There is a greedy strategy: If the next part is not muddy, jump to it else jump to the second next part.
By using that simple strategy, the expected number of muddy parts visited can be found with a recurrence that for each part, picks the probability the next part is muddy and then gets the appropriate value. The recurrence is easy to implement iteratively.
感悟: 这个地方线性解决了这个问题.. (用的是贪心的策略.. 感觉很难想)ppt中都是用dp做的.
贪心策略, 如果i+1是 muddy 那么去i+2更好, 如果i+1不是muddy 那么去i+1更好, 然后题目中有两个函数, 一个N[x] p[x];
public double getExpectedValue(int[] road)
{
int n = road.length;
double[] p = new double[n];
for (int i=0; i<n; i++) {
p[i] = road[i] / 100.0;
}
//N[x] is the expected number of muddy parts
//we will have to step in if starting at
//part x. Without counting part x's mud.
double[] N = new double[n];
N[n-1] = 0.0;
N[n-2] = p[n-1]; //p[n-1] is actually always
// 0 due to the constraints.
for (int i=n-3; i>=0; i--) {
//If the next part is muddy, jump to i+2
//Which could be muddy with probability p[i+2]
N[i] = p[i+1] * ( p[i+2] + N[i+2] );
//If the next part is not muddy jump to it.
N[i] += (1-p[i+1]) * N[i+1];
}
// p[0] will also always be 0 due to the constraints.
return N[0] + p[0];
}