LightOJ 1274 Beating the Dataset (概率dp)

题意:给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
#include
#include
#include
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;
}


你可能感兴趣的:(LightOJ 1274 Beating the Dataset (概率dp))