题目link:http://acdream.info/problem?pid=1113
小记:当时比赛晓得了是概率dp,但是头脑不清醒,没做。现在想来,当时的思路确实是对的
思路:解题报告的思路是:
dp[i] = dp[i+1]/6 + dp[i+2]/6 + dp[i+3] /6+...+ dp[i+6]/6 + 1
对特殊的几个,例:
dp[n-3] = dp[n-3]/2 + dp[n-2]/6 + dp[n-1]/6 + dp[n]/6 + 1;
即在n-3这个位置,有1/2的概率不变, 1/6的概率加1, 1/6的概率加2,1/6的概率加3, 相乘后得到平均期望 然后加上其投出的这一次 就是投出i的期望值
这个状态转移方程的最后结果即是 dp[0],
我的思路是反过来的
dp[i] = dp[i-1]/6 + dp[i-2]/6 +...+ dp[i-6]/6 + 1
我觉得这样更符合人的思维些, 我当前这个i值,是有1/6从i-1过来的,1/6从i-2....等等
对于最开始时,如:
dp[3] = dp[3]/2 + dp[2]/6 + dp[1]/6 + dp[0]/6 + 1
解方程即是 左右乘以个 6, 然后计算是否有大于n 或小于0 的投掷,计算次数,用于计算结果
代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #include <map> #include <set> #include <vector> #include <stack> #include <queue> #include <algorithm> using namespace std; #define mst(a,b) memset(a,b,sizeof(a)) #define REP(a,b,c) for(int a = b; a < c; ++a) #define eps 10e-8 const int MAX_ = 100010; const int N = 100010; const int INF = 0x7fffffff; double d[MAX_]; int main(){ //freopen("f:\\in.txt","r", stdin); //freopen("f:\\out.txt", "w", stdout); int T, n; scanf("%d", &T); while(T--){ mst(d, 0); scanf("%d", &n); d[0] = 0; REP(i, 1, n+1){ int cnt = 0; REP(j, 1, 7){ if(i - j >= 0){ d[i] += d[i-j]; } else cnt++; } //printf("%d\n", cnt); d[i] += 6; d[i] /= (6-cnt); } printf("%.2f\n", d[n]); } return 0; }