#include <string.h>
double DP[50][50];
double P[1010];
double PSolveN[1010][50];
double PSolveNAcc[1010][50];
void getMENP(double * array, int N, int length, int M, double (* PSolveN)[50] ) // MENP: more/equal N questions probability
{
// printf("N %d length %d M %d\n", N, length, M);
// if (N > length) {
// return 0;
// }
memset(DP, 0, sizeof(DP));
int i = 0;
for (; i <= length; i++) {
int j = 0;
for (; j<=M; j++) {
if (i < j) { // imposible
DP[i][j] = 0;
} else if (i == 0 && j == 0) { // must be
DP[i][j] = 1;
} else if (j == 0) {
DP[i][j] = (1-array[i-1]) * DP[i-1][j];
} else {
double p1 = DP[i-1][j];
double p2 = DP[i-1][j-1];
double solveThisP = p2*array[i-1];
double noSolveThisP = p1*(1- array[i-1]);
DP[i][j] = solveThisP + noSolveThisP;
// printf("%d %d %lf\n", i, j, DP[i][j]);
}
}
}
for (int j = 0; j <=length; j++) {
(*PSolveN)[j] = DP[length][j];
}
}
int main() {
int M;
int T;
int N;
while(1){
memset(P, 0, sizeof(P));
memset(PSolveN, 0, sizeof(PSolveN));
memset(PSolveNAcc, 0, sizeof(PSolveNAcc));
double res = 0;
double AllME1P = 1;
double AllLNME1 = 1;
scanf("%d %d %d", &T, &M, &N);
if (0 == M &&
0 == T &&
0 == N) {
return 0;
}
if (T < N) {
printf("%.3f\n", 0.0);
continue;
}
int i = 0;
for (; i < M; i++) {
int j = 0;
for (; j < T; j++) {
scanf("%lf", P+j);
}
getMENP(P, N, T, M, PSolveN + i);
}
for (i = 0; i< M; i++) {
int j = 0;
for (; j <= T; j++) {
if (0 == j) {
PSolveNAcc[i][j] = PSolveN[i][j];
} else {
PSolveNAcc[i][j] = PSolveN[i][j] + PSolveNAcc[i][j-1];
}
// printf("%d %d %.3lf ", i ,j, PSolveNAcc[i][j]);
}
// printf("\n");
}
for (i = 0; i < M; i++) {
AllME1P *= (1-PSolveN[i][0]);
}
for (i = 0; i < M; i++) {
AllLNME1 *= (PSolveNAcc[i][N-1] - PSolveNAcc[i][0]);
}
res = AllME1P - AllLNME1;
// printf("%f %f\n", AllME1P, AllLNME1);
printf("%.3f\n", res);
}
}
G++ 1188K 79MS.
悲催一题, 因为RE卡了半天, 后来才发现,自己把 T(队伍数量 1~1000)和M(题目数量 1~ 30) 搞混了, 这样必然后面数组访问会出错,
当时发现了这个问题,输入时搞颠倒了,但是没有接着把用到的数组的尺寸调换,妈的,真是2B的超出了自己的想象, 后面为了排查RE,直接把poj当成调试器用了...
悲剧的是,最后还是靠直接看代码发现的这个问题.... 唉, 发现现在自己现在思维有问题,写完code,就懒得再仔细的检查,直接上数据,有错了再用数据debug,
诚然, 很多算法题第一遍就就写对的难度还是蛮高的,但是不能因为这个就完全指望测试,测试终究有自己的覆盖范围(poj 很多AC的最后其实还是有问题的)。
以后必须要强化审阅code,就像现在工作中一样,不能指望别人code rebview给你发现问题,一个合格的程序员,必须在自己脑子里对自己的code有最清晰和肯定的认识.
唉,其实我现在有时候就是code AC了, 还是没底,一定要消除这个问题了.
回到题本身, 这道题我在刷题指南里看到,被分到了hash,其实应该不是, 硬要归,就是概率论+DP.
DP在这里的作用就是求出了 某一队在整场比赛解出k道题的概率, 一开始还想着按照传统数学思路来,后来发现是作死,看了discuss以后一想,确实DP可以用在这里:
以前解DP题,大都是求最优解,及每个解之间是要比较的,但这只是DP的一个种类,有些DP, 不是要求最优解,而是子问题解之间的一种组合。
对某一队(解题概率数组P)来说, 定义D[i][j]为该队在前i道题中解出j道题的概率, 那么,就有:
D[i][j] = D[i-1][j]*(1- P[i])<第i题没有解出来,前i-1道题已经解出了j道 > + D[i-1][j-1]*P[i]<解出第i道题, 前i-1道题解出了j-1道>, 其实正好是一个概率组合.
对某些特殊情况:
i< j : 不可能, 0;
i== 0 && j == 0: 一定会发生, 1;
i>0 & j == 0, D[i][j] = D[i-1][j]*(1- P[i]) D[i-1][j-1] 是没有意义的情况(做出的题是-1道), 从概率论上考虑,做到第i道解出0道题的概率一定等于(1-P[i]) *<解到前i-1题,解出0道的概率>, 因为不可能解出负数的题.
这个数组求出来以后,取 i= M的情况,那么就表示的是 在比赛中,该队解出第 0, 1 ,2 ,3 ... M道题的概率 ,其实一个二维数组了,以为每一队都有一个自己的这样的数组.
那么,根据题意, 只有每一队解出了>=1道题, 且至少由一队解出了>=N道题(注意,这里是至少一队, 这就意味着有多个队解出了同样数量的>=N的题也是合理的解).
哎,这时候显出概率论的不足了, 想了很长时间都找不到一个简单的表达合理解的概率组合方式.
后来在想出来,满足解的概率应该是这样:
每一队都解出了>=1道题的概率 - 每一队都解出了>=1道的题,但是每一对只解出了<N道题.
有了之前解出的前i道题解出j道题概率数组PSolveN,进行一次处理:
用PSolveNAcc[i][j] 表示对于第i队,在比赛中 至少解出了j道题的概率(及解出了0, 1 ,2 ..j 道题的概率的总和),
那么PSolveNAcc[i][j] = PSolveN[i][j] + PSolveNAcc[i][j-1].
最后就是做个减法即可.