【LightOJ 1274】Beating the Dataset(概率DP)
题目大意:
一道题目有多个输出文件。
每个文件有一个输出,”YES”或”NO”
现在直接提交答案,从”YES”开始,如果与后台答案不同时,下一次输出”NO”,以此类推,如果相同则接着输出上一次的答案。
给出n,表示后台n个输出,s表示输出文件的总大小,一个”YES”占3Byte
一个”NO”占2Byte
问判完最后一个输出后,错误的组数的期望。
初步思路:
dp[i][0]
表示当前是第i字节,并且下一步要输出YES
dp[i][1]
表示当前是第i字节,并且下一步要输出NO
后来发现输出爆炸,原因是这样来做,固定的s最终的结果会对应多种n,但固定n和s后,YES和NO的数量就固定了。
对啊!YES和NO固定的。思路2:
dp[i][j][0]
表示i个YES j个NO,下一步输出YES
dp[i][j][1]
表示i个YES j个NO,下一步输出NO
开不下。然后想到用时间换空间,但是这样时间也已经很爆炸了!
可能最近比较频繁做类似的题,想到边遍历边转移。也就是把一维状态在for循环中表现出来。
从1到n遍历,表示当前剩下i个文件没评测。
第二层循环YES的个数,表示剩下j个文件是YES
这不就是滚动数组吗!
然后转移搞一下就出来了
代码如下:
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
const int msz = 5555;
double dp[msz][2];
int main()
{
int t,n,s;
scanf("%d",&t);
for(int z = 1; z <= t; ++z)
{
scanf("%d%d",&n,&s);
//Num of "YES"
int a = s-2*n;
memset(dp,0,sizeof(dp));
for(int i = 1; i <= n; ++i)
{
int p = i;
//printf("%d:\n",i);
for(int j = a; j >= 0; --j)
{
//i is "NO"
if(j < i)
{
dp[j][0] = (dp[j][1]+1)*(i-j)/p;
dp[j][1] = dp[j][1]*(i-j)/p;
}
else dp[j][1] = dp[j][0] = 0;
//i is "YES"
if(j >= 1 && j <= i)
{
dp[j][0] += dp[j-1][0]*j/p;
dp[j][1] += (dp[j-1][0]+1)*j/p;
}
//printf("prt:YES useYES:%d %f\n",j,dp[j][0]);
//printf("prt:NO useYES:%d %f\n",j,dp[j][1]);
}
}
printf("Case %d: %.11f\n",z,dp[a][0]);
}
return 0;
}