概率dp其实就是利用动态规划的思想去解决概率、期望等题目,本质上来说与普通的dp没有太大的区别,只是可能会涉及到一些概率论方面的知识。so,练题吧......
入门题1,HDOJ:3853,时空转移(点击打开链接),题目如下:
#include
using namespace std;
double p[1005][1005][3], dp[1005][1005];
int main()
{//freopen("sample.txt", "r", stdin);
int r, c;
while(~scanf("%d%d", &r, &c))
{
for(int i=1; i<=r; ++i)
for(int j=1; j<=c; ++j)
for(int k=1; k<=3; ++k)
scanf("%lf", &p[i][j][k]);
dp[r][c] = 0; // 处理边界
for(int i=r; i>0; --i) // 从后往前递推
for(int j=c; j>0; --j)
{
if(p[i][j][1]==1 || (i==r&&j==c))
continue;
dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2) // 状态转移,注意需要加上本次花费的2
/ (1-p[i][j][1]);
}
printf("%.3f\n", dp[1][1]);
}
return 0;
}
题意:
有0-n个格子,初始点在0,终点是>=n,每走一步之前都要丢一次六个面的色子,标上1-6,扔到几就走几步,当然色子是等概率出现数字的,还有就是中间某一点可能和其它的一点联通,比如a和b联通,当我处于a时,就可以直接飞到b,最后问走到终点时所扔色子次数的期望。
分析:
这道题目和上一道很像,仅仅是走法出现的概率产生方式不同,上一道是直接给出,而本题是扔色子,其实概率也就是1/6,但是本题还有一个不同的地方,那就是可以飞行,比如从a飞到b,而不用扔色子,这些条件都会影响我们做题的方法。仿照上一题,首先定义状态dp[i]表示处于i位置时所扔色子次数的期望,可知dp[n] = 0。然后找找转移方程,那根据题意可知,i位置要么是可以飞行的,要么就不是,所以当i点可以飞行时,dp[i] = dp[fly[i]],其中fly[i]代表i飞行到的另一个点,否则,依据期望的定义,dp[i] = 1/6*dp[i+j] + 1,其中j = 1,2...6,代表扔色子可能的点数,最后别忘了加1,代表本次需要扔一次色子。最后,规划方向依然是从后往前,答案就是dp[0]。
源代码:
#include
using namespace std;
const int MAXN = 100005;
double dp[MAXN];
int fly[MAXN];
int main()
{//freopen("sample.txt", "r", stdin);
int n, m;
while(~scanf("%d%d", &n, &m) && (n||m))
{
for(int i=0; i<=n; ++i)
{
dp[i] = 0;
fly[i] = -1;
}
int a, b;
for(int i=0; i=0; --i)
{
if(fly[i] != -1)
dp[i] = dp[fly[i]]; // 转移方程1
else
{
for(int j=1; j<=6; ++j) // 转移方程2
if(i+j >= n) // 注意走的步数可能超过n,但统一使用dp[n]
dp[i] += 1.0/6 * dp[n];
else
dp[i] += 1.0/6 * dp[i+j];
++dp[i];
}
}
printf("%.4f\n", dp[0]);
}
return 0;
}
入门题3,POJ:2096,时空转移(点击打开链接),题目如下:
Time Limit: 10000MS | Memory Limit: 64000K | |
Total Submissions: 2634 | Accepted: 1284 | |
Case Time Limit: 2000MS | Special Judge |
Description
Input
Output
Sample Input
1 2
Sample Output
3.0000题意:
有n类bug和s个子系统,bug数量不限,且每天只能发现一个bug,要求的是当在s个子系统中发现n类bug时所需要天数的期望(平均天数)。
分析:
先确定状态,假设dp[i][j]表示已经在j个子系统中发现i类bug时所用天数的期望,明显dp[n][s] = 0。然后推导状态之间的转移,依据dp[i][j]的含义我们不难发现,下一天发现bug的情况只可能是以下四种情况:
1、在新的子系统中发现新的bug,即dp[i+1][j+1]
3、在已经发现过bug的子系统中发现新的bug,即dp[i+1][j]
2、在新的子系统中发现已经发现过的bug,即dp[i][j+1]
4、在已经发现过bug的子系统中发现已经发现过的bug,即dp[i+1][j+1]
同样,不难得出上述四种情况对应的概率分别为:p1 = (n-i)*(s-j) / (n*s),p2 = (n-i)*(j) / (n*s),p3 = i*(s-j) / (n*s),p4 = i*j / (n*s)。
综上,我们的状态转移方程就出炉了,同样根据期望的定义,dp[i][j] = p1*dp[i+1][j+1] + p2*dp[i+1][j] + p3*dp[i][j+1] + p4*dp[i][j] + 1,移项合并一下,dp[i][j] = (p1*dp[i+1][j+1] + p2*dp[i+1][j] + p3*dp[i][j+1] + 1) / (1-p4)。这样一来,我们要求的答案就是dp[0][0]。顺便说一句,这道题不是和第一题一样的么!仅仅是下一个状态的情况稍微隐含一些!
源代码:
#include
#include
using namespace std;
const int MAXN = 1005;
double dp[MAXN][MAXN];
int main()
{//freopen("sample.txt", "r", stdin);
int n, s;
while(~scanf("%d%d", &n, &s))
{
double p1, p2, p3, p4;
memset(dp, 0, sizeof(dp));
for(int i=n; i>=0; --i)
for(int j=s; j>=0; --j)
{
if(i==n && j==s)
continue;
p1 = 1.0*(n-i)*(s-j) / (n*s);
p2 = 1.0*(n-i)*j / (n*s);
p3 = 1.0*i*(s-j) / (n*s);
p4 = 1.0*i*j / (n*s);
dp[i][j] = (p1*dp[i+1][j+1] + p2*dp[i+1][j] // 状态转移
+ p3*dp[i][j+1] + 1)
/ (1-p4);
}
printf("%.4f\n", dp[0][0]);
}
return 0;
}
入门题4,POJ:3071,时空转移(点击打开链接),题目如下:
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 3147 | Accepted: 1593 |
Description
Consider a single-elimination football tournament involving 2n teams, denoted 1, 2, …, 2n. In each round of the tournament, all teams still in the tournament are placed in a list in order of increasing index. Then, the first team in the list plays the second team, the third team plays the fourth team, etc. The winners of these matches advance to the next round, and the losers are eliminated. After n rounds, only one team remains undefeated; this team is declared the winner.
Given a matrix P = [pij] such that pij is the probability that team i will beat team j in a match determine which team is most likely to win the tournament.
Input
The input test file will contain multiple test cases. Each test case will begin with a single line containing n (1 ≤ n ≤ 7). The next 2n lines each contain 2n values; here, the jth value on the ith line represents pij. The matrix P will satisfy the constraints that pij = 1.0 − pji for all i ≠ j, and pii = 0.0 for all i. The end-of-file is denoted by a single line containing the number −1. Note that each of the matrix entries in this problem is given as a floating-point value. To avoid precision problems, make sure that you use either the double
data type instead of float
.
Output
The output file should contain a single line for each test case indicating the number of the team most likely to win. To prevent floating-point precision issues, it is guaranteed that the difference in win probability for the top two teams will be at least 0.01.
Sample Input
2 0.0 0.1 0.2 0.3 0.9 0.0 0.4 0.5 0.8 0.6 0.0 0.6 0.7 0.5 0.4 0.0 -1
Sample Output
2
Hint
In the test case above, teams 1 and 2 and teams 3 and 4 play against each other in the first round; the winners of each match then play to determine the winner of the tournament. The probability that team 2 wins the tournament in this case is:
P(2 wins) | = P(2 beats 1)P(3 beats 4)P(2 beats 3) + P(2 beats 1)P(4 beats 3)P(2 beats 4) = p21p34p23 + p21p43p24 = 0.9 · 0.6 · 0.4 + 0.9 · 0.4 · 0.5 = 0.396. |
The next most likely team to win is team 3, with a 0.372 probability of winning the tournament.
有2^n支队,现在要进行n次比赛,并且按次序进行比赛并淘汰,胜利的队继续按次序比赛并淘汰,比如1,2,3,4进行比赛,第一轮1和2比,3和4比,假如1和3胜利了,那么第二轮1和3继续比,2,4淘汰。最后问最有可能胜利的队伍是哪一支。
分析:
分析题目,先创建状态,dp[i][j],表示在第i轮比赛中第j支队获胜,那么dp[0][j] = 1。然后考虑状态间的转移,很明显,如果j队要在本轮中获胜,前提是在上一轮中必须要先获胜才有资格,即dp[i-1][j],并且在本轮中要击败所有可能的对手,那同时也要求对手也要在上一轮中获胜,才有资格进入本轮,即p[j][k]*dp[i-1][k],其中p[j][k]代表j击败k的概率,所以状态转移方程为:dp[i][j] = dp[i-1][j] * (dp[i-1][k]*p[j][k]),其中k为本轮与j比赛的队伍。可是,不知道大家发现没有,这里有一个问题,那就是题目中说的淘汰制度,很明显必须要保证本轮比赛的双方曾经是没有比赛过的,因为如果两队曾经遇到过,必然会淘汰一队,那么现在又怎么可能再次比赛呢?所以在进行状态转移之前必须加一个条件,保证j和k是第一次进行比赛,那当我们将比赛流程用二进制表示时,会发现规律,当j>>(i-1) 等于 (k>>(i-1))^1时,j和k在第i轮比赛中第一次相遇。
源代码:
#include
#include
using namespace std;
const int MAXN = 150;
double dp[MAXN][MAXN], p[MAXN][MAXN];
int main()
{//freopen("sample.txt", "r", stdin);
int n, num;
while(~scanf("%d", &n) && n!=-1)
{
num = 1 << n;
memset(dp, 0, sizeof(dp));
for(int i=0; i>(i-1)) == ((k>>(i-1))^1)) // 转移条件
dp[i][j] += dp[i-1][j] * (dp[i-1][k]*p[j][k]); // 状态转移方程
int ans = 0;
for(int i=1; i dp[n][ans])
ans = i;
printf("%d\n", ans+1);
}
return 0;
}