LightOJ 1274 Beating the Dataset (概率dp)

来源:互联网 发布:尤克里里软件哪个好 编辑:程序博客网 时间:2024/06/06 11:52

题意:给n和s,表示有n个题目,对应n个答案分别为yes或no,yes为3bytes,no为2bytes,s表示所有答案总的byte大小

根据所给的条件,很容易得出有多少个yes和no

接下来你要做的事就是猜答案,那么你猜答案的规则是把某个可能的答案序列拿出来,在首位加上一个yes,去掉最后一个,再用得到的答案序列与可能的答案序列进行比较,某一个答案不相同表示一个错误答案

题目要求输出的是错误答案个数的期望

那么考虑dp[i][j][k],表示第i位使用了j个yes下一位输出yes(k=0)或no(k=1)的期望,期望使用逆推

因为可以预先求出yes和no的个数,那么当前第i位输出yes的期望即为i+1位输出yes的期望与i+1位输出no的期望+1然后分别乘以出现的概率

换成公式可以表示为

dp[i][j][0] = dp[i+1][j+1][0]*p1 + (dp[i+1][j][1]+1)*p2;
dp[i][j][1] = (dp[i+1][j+1][0]+1)*p1 + dp[i+1][j][1]*p2;

p1表示下一位输出yes的概率,p2表示下一位输出no的概率

因为要把可能的答案序列首位加yes,所以从n-1~0枚举第一层,表示变换后得到的序列

此处因为n为5000,所以使用滚动数组,降为dp[j][k]即可

代码如下:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int maxn = 5000+7;int n,s;double dp[maxn][2];int main() {    int T;    scanf("%d",&T);    for(int kas=1; kas<=T; ++kas) {        scanf("%d%d",&n,&s);        int yes = s-2*n, no = 3*n-s;        memset(dp,0,sizeof(dp));        for(int i=n-1; i>=0; --i) {            int base = n-i;            int R = min(yes,i), L = max(i-no,0);            for(int j=L; j<=R; ++j) {                double p1 = 1.0*(yes-j)/base, p2 = 1.0*(no-(i-j))/base;                double a = dp[j+1][0]*p1 + (dp[j][1]+1)*p2;                double b = (dp[j+1][0]+1)*p1 + dp[j][1]*p2;                dp[j][0] = a;                dp[j][1] = b;            }        }        printf("Case %d: %.10f\n",kas,dp[0][0]);    }    return 0;}