LightOJ 1274 Beating the Dataset

来源:互联网 发布:软件采购制度 编辑:程序博客网 时间:2024/06/09 17:19

LightOJ 1274
这道题真的不太好懂啊>_<,果然英语太差也不能愉快的刷题
题意:
某个人在比赛的时候发现没时间啦,但是他知道这道题只会输出yes 和 no,yes占三个字节,no占两个字节,已知输出文本的字节数,然后他想通过猜答案的方式解决这道题。。。
题目有T组数据,每组数据有两个输入,n和s,分别代表yes和no的总数以及输出文本的字节数,在他猜的答案(全是yes和no的字符串)的基础上,由于他猜的答案并不是通过正常的逻辑输出在answer.txt文件中的,所以程序会自动为他输出一个yes,也即是在他猜的所有答案中,头部要加入一个yes,这跟他猜的答案无关,是程序自动生成的一个答案,要求求他错的样例数
1 <= n <= 5000 && 2 * n <= s <= 3 * n

在开始看这道题的时候脑子里面一片空白。。。好绕啊

然后我们简化一下,yes和no可以简化为1 和 0,这个题的意思就是你已知1和0的个数,这些排列你随心定,在你写出的01串之前加上一个1,问你两个01串的不匹配数(比如说你拟出来的串是0100,首部加上一个1得到的串是10100,当然最后一个0是不用参与匹配的,相当于0100串与1010串比较得到不匹配的字符数)就是这样,有很多种01字符串的可能。
该匹配是自身匹配,相当于前一个字符与后一个字符对比,如果是不同字符不能匹配数就+1
毫无疑问,最开始我们要做的工作就是先通过输入数据求出1和0的个数,设a为1的个数,b为0的个数,我们要构造一个具有a个1的01字符串,字符串长度为n,在每一次添字符的时候都有两种可能(当还能添1的时候)0和1。
我们需要保存三个状态:
1.现已访问到串的哪个位置
2.前一个字符是什么
3.已经添过多少个字符1了
dp[i][j][k]保存的是:当遍历到i这个位置,已经有j个字符1,要加入的字符为k(0, 1)的不匹配数

开始我想的是开一个dp[MAXN][MAXN][2]的数组=_=…当然这样是不行的,进一步看,i是用来保存字符串到哪个位置,后一个位置会受该位置的影响,所以i开MAXN是没有必要的,我们需要的状态只是相邻的一位,这种思想emmm貌似是叫滚动数组
(0 _ 0)…
我想的是倒着推,由于我们在i这个位置,我们需要与后一个位置的字符比较,所以在遍历到i这个位置,i+1位置的dp值我们需要知道,倒推就能做到这点。
然后我们先不去管首位置的那个1
关键的就是状态转移方程了:
p1指抽取到1的概率,p0指抽取到0的概率
dp[i][j][0] = (dp[1 - i][j + 1][1] + 1) * p1 + (dp[1 - i][j][0]) * p0;
dp[i][j][1] = (dp[1 - i][j + 1][1]) * p1 + (dp[1 - i][j][0] + 1) * p0;
(1 - i是什么? —后一个字符的位置; 为什么+1? —当前后匹配不上的时候,dp需要加一;) 由于不断的在取字符,所以p1与p0的值也是随之改变的

我很智障的在算概率的时候没有进行强制类型转换,以至于老是不对。。。

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int MAXN = 5e3 + 10;double dp[2][MAXN][2];int T, n, s;int main() {  scanf("%d", &T);  int Case = 0;  while (T--) {    scanf("%d %d", &n, &s);    int a = s - 2 * n, b = 3 * n - s;    memset(dp, 0, sizeof dp);    for (int i = n - 1; i >= 1; i--) {      int maxm = min(i, a), minm = max(i - b, 0);      //maxm与minm是用来确定还能不能加一的      memset(dp[i & 1], 0, sizeof dp[i & 1]);      /*当前位置的值初始化为零,加不加这个都能过,不过逻辑上是需要这句话的,排除前面的影响*/      for (int j = minm; j <= maxm; j++) {        double lasty = (double)(a - j) / (n - i); //出现yes的概率        double lastn = (double)(b - (i - j)) / (n - i); //出现no的概率        int now = i & 1, nxt = 1 - now; //当前位置的值        dp[now][j][1] = dp[nxt][j + 1][1] * lasty                          + (dp[nxt][j][0] + 1) * lastn;        dp[now][j][0] = (dp[nxt][j + 1][1] + 1) * lasty                          + dp[nxt][j][0] * lastn;      }    }    cout << "Case " << ++Case << ": ";    printf("%.7lf\n", a * dp[1][1][1] / n + b * (dp[1][0][0] + 1) / n);  }}

这个题好像还有一种很厉害的解法。。。推的那个数学公式很简单,可是我不知道那是怎么推出来的。。。

详情请参考下面链接http://www.cnblogs.com/lonewanderer/p/5746649.html

原创粉丝点击