概率与期望 HDU 4405 Aeroplane chess(飞行棋)

来源:互联网 发布:js改变color rgb 编辑:程序博客网 时间:2024/05/24 07:11

题目:

Description

Hzz loves aeroplane chess very much. The chess map contains N+1 grids labeled from 0 to N. Hzz starts at grid 0. For each step he throws a dice(a dice have six faces with equal probability to face up and the numbers on the faces are 1,2,3,4,5,6). When Hzz is at grid i and the dice number is x, he will moves to grid i+x. Hzz finishes the game when i+x is equal to or greater than N. 

There are also M flight lines on the chess map. The i-th flight line can help Hzz fly from grid Xi to Yi (0<Xi<Yi<=N) without throwing the dice. If there is another flight line from Yi, Hzz can take the flight line continuously. It is granted that there is no two or more flight lines start from the same grid. 

Please help Hzz calculate the expected dice throwing times to finish the game. 

Input

There are multiple test cases. 
Each test case contains several lines. 
The first line contains two integers N(1≤N≤100000) and M(0≤M≤1000). 
Then M lines follow, each line contains two integers Xi,Yi(1≤Xi<Yi≤N).   
The input end with N=0, M=0. 

Output

For each test case in the input, you should output a line indicating the expected dice throwing times. Output should be rounded to 4 digits after decimal point. 

Sample Input

2 08 32 44 57 80 0

Sample Output

1.16672.3441

这个题目还是有点复杂的。

不过有一个地方可以化简,那就是,因为我们基本上都是用累加刷表的形式来算的,所以如果有2条飞行线(flight line)连起来,这个是不用考虑的。

也就是说,我们就当没有这种现象,来写代码就可以了,如果有这种现象出现,答案也是一样的。

我的第一种思路是,用list记录到底某个格子的概率,用times记录从0到这个格子所需要的平均次数。

代码:

#include<iostream>#include<string.h>#include<iomanip>using namespace std;int n, m;double list[100011];//概率double times[100011];//平均次数int fly[100011];//飞行线double f(int i,int k){if (i < 0)return 0;if (i >= n - 5 && k == n)return list[i] * (7 - n + i) / 6;return list[i] / 6;}double ft(int i,int k){if (i < 0)return 0;return f(i, k) * (times[i] + 1);}int main(){int start, end_;double r1, r2;while (cin >> n >> m){if (n == 0)break;memset(fly, 0, sizeof(fly));for (int i = 0; i < m; i++){cin >> start >> end_;fly[start] = end_;}memset(list, 0, sizeof(list));memset(times, 0, sizeof(times));list[0] = 1;times[0] = 0;for (int i = 1; i <= n; i++){list[i] += f(i - 1, i) + f(i - 2, i) + f(i - 3, i) + f(i - 4, i) + f(i - 5, i) + f(i - 6, i);times[i] += ft(i - 1, i) + ft(i - 2, i) + ft(i - 3, i) + ft(i - 4, i) + ft(i - 5, i) + ft(i - 6, i);if (list[i] > 0)times[i] /= list[i];if (fly[i]){list[fly[i]] += list[i];times[fly[i]] += list[i] * times[i];list[i] = 0;}}cout << fixed << setprecision(4) << times[n] << endl;}return 0;}

这个是78ms的

因为在AC之前出现过几次wrong answer,我以为是除0的问题,所以在这个代码完成之前我是用另外一个代码AC的。

我的第二种思路:times不表示次数,而表示次数的期望。

代码:

#include<iostream>#include<string.h>#include<iomanip>using namespace std;int n, m;double list[100011];//概率double times[100011];//次数的期望int fly[100011];//飞行线double f(int i,int k){if (i < 0)return 0;if (i >= n - 5 && k == n)return list[i] * (7 - n + i) / 6;return list[i] / 6;}double ft(int i,int k){if (i < 0)return 0;if (list[i] == 0)return 0;if (i >= n - 5 && k == n)return (times[i] + list[i])*(7 - n + i) / 6;return (times[i] + list[i]) / 6;}int main(){int start, end_;double r1, r2;while (cin >> n >> m){if (n == 0)break;memset(fly, 0, sizeof(fly));for (int i = 0; i < m; i++){cin >> start >> end_;fly[start] = end_;}memset(list, 0, sizeof(list));memset(times, 0, sizeof(times));list[0] = 1;times[0] = 0;for (int i = 1; i <= n; i++){list[i] += f(i - 1, i) + f(i - 2, i) + f(i - 3, i) + f(i - 4, i) + f(i - 5, i) + f(i - 6, i);times[i] += ft(i - 1, i) + ft(i - 2, i) + ft(i - 3, i) + ft(i - 4, i) + ft(i - 5, i) + ft(i - 6, i);if (fly[i]){list[fly[i]] += list[i];times[fly[i]] += times[i];list[i] = 0;}}cout << fixed << setprecision(4) << times[n] << endl;}return 0;}

这个代码最后一次的修改是最后的 if 里面的代码。AC是140ms。

因为2个代码几乎是差不多的,所以我把前面那个代码也对应的改了交了AC了。(本文的3个代码都是AC了的)

在我写到这里的时候,惊奇的发现,求期望我居然是从0往后顺着推的!

明明应该从n往前倒推啊啊啊。。。

然后我又写了一下逆推的算法,只写了2分钟就写完了,提交直接AC,而且只有31ms。逆推比顺推简单太多太多了。

这里的times的意思又不一样,指的是从每个格子到底终点所需要的平均次数。

代码:

#include<iostream>#include<string.h>#include<iomanip>using namespace std;int n, m;double times[100011];//平均次数int fly[100011];//飞机int main(){int start, end_;double r1, r2;while (cin >> n >> m){if (n == 0)break;memset(fly, 0, sizeof(fly));for (int i = 0; i < m; i++){cin >> start >> end_;fly[start] = end_;}memset(times, 0, sizeof(times));for (int i = n - 1; i >= 0; i--){if (fly[i])times[i] = times[fly[i]];else times[i] = (times[i + 1] + times[i + 2] + times[i + 3] + times[i + 4] + times[i + 5] + times[i + 6]) / 6 + 1;}cout << fixed << setprecision(4) << times[0] << endl;}return 0;}

关于为什么反向求比正向求要简单的多的多,请点击查看我的博客

2 0