题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6558
解题心得:
- 首先这是个没有止境的游戏,但是在某一个状态答案是已知的,然后从这个已知的答案,往回推可以得到最终的结果。
- 就这个题来说已知的状态很明显,就是最后只要获胜中奖概率为 100 % 100\% 100%,这个时候期望是已知的就是 1 / p 1/p 1/p,为啥,例如一个硬币抛起来正反面的概率都是 1 / 2 1/2 1/2,你想要得到正面,期望的次数就是 2 2 2。
- 然后这个题转移状态就比较简单了,只是有两个状态可能稍微有些绕,但是可以直接写记忆化,这样思维比较清晰,当然对大佬来说直接推 D P DP DP方程也很简单。还要注意一下因为有个 1.5 % 1.5\% 1.5%,这里可以用 200 200% 200为最终态。 D P [ i ] DP[i] DP[i]为中奖几率为 i i i时期望的游戏轮数。
记忆化代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500;
double dp[maxn];
int p;
double P;
double dfs(int q) {
if(q > 200) q = 200;
double Q = double(q)/200;
if(q == 200) return dp[q] = double(100)/p;
if(dp[q] > -1) return dp[q];
return dp[q] = double(1) + P * (1-Q)* dfs(q+4) + (1-P)*dfs(q+3);//当前未中奖多一次以及未中奖的所有可能
}
int main() {
// freopen("1.in.txt", "r", stdin);
int t; scanf("%d", &t);
int T = t;
while(t--) {
for(int i=0;i<maxn;i++) dp[i] = -1;
scanf("%d", &p);
P = double(p)/100;
printf("Case %d: %.10f\n",T-t, dfs(4));
}
return 0;
}
DP代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500;
double dp[maxn];
int p;
int main() {
// freopen("1.in.txt", "r", stdin);
int t; scanf("%d", &t);
int T = t;
while(t--) {
for(int i=0;i<maxn;i++) dp[i] = 0.0;
scanf("%d", &p);
double P = double(p)/100.0;
dp[200] = double(1)/P;
for(int i=199;i>=0;i--) {
double Q = double(i)/200;
dp[i] = P*(Q + (1.0-Q)*(1.0+dp[min(200, i+4)]));
dp[i] += (1.0-P) * (1.0 + dp[min(200, i+3)]);
}
printf("Case %d: %.10f\n",T-t, dp[4]);
}
return 0;
}