POJ 1189

教训很多也收获不少的一道题

首先是DP状态的定义,开始一直不知道怎么下手写,后来觉得这个定义比较合适(dp(i, j))定义为当前假设第i行全是没拔掉钉子,落在第i行第j个钉子上的钉子的数量(概率分数处理会很麻烦)

然后是数学上的两个问题:

  • 一是概率,想当然的计算从顶向下方法数,结果测试数据一直发现不对劲,就是概率的理解出现了偏差,check别人的代码恍然大悟后利用模拟的思路,假设顶上有(1<<n)落下来这样模拟。反思计算方法数从而求得概率,应该是相关的条件概率在其中出现了问题,导致计算方法数是错误的想法
  • 二是gcd,简单复习了下

此外,代码中最后循环求和实际测试比直接复制(1<<n)快就比较玄学...

中间WA了三次,主要问题是两处,数据范围没有仔细斟酌,应该使用long long,另一处则是对于0的特殊处理

#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <set>
using namespace std;

typedef unsigned long long LL;
const int maxn= 55;

char bd[maxn][maxn];
LL dv[maxn][maxn];

inline LL GCD(LL q, LL r)
{
	return 0== r ? q : GCD(r, q%r);
}

int main(int argc, char const *argv[])
{
	int n, m;
	while (~scanf("%d %d", &n, &m)){
		for (int i= 1; i<= n; ++i){
			for (int j= 1; j<= i; ++j){
				scanf(" %c", bd[i]+j);
			}
		}

		dv[1][1]= (((LL)1)<<n);
		++n;
		for (int i= 2; i<= n; ++i){
			for (int j= 1; j<= i; ++j){
				dv[i][j]= 0;
				if (j!= i && '*'== bd[i-1][j]){
					dv[i][j]+= (dv[i-1][j])>>1;
				}
				if (j> 1){
					if ('*'== bd[i-1][j-1]){
						dv[i][j]+= (dv[i-1][j-1])>>1;
					}
					if ('.'== bd[i-2][j-1]){
						dv[i][j]+= dv[i-2][j-1];
					}
				}
			}
		}
		LL sum= 0, ans= dv[n][m+1], gcd;
		for (int i= 1; i<= n; ++i){
			sum+= dv[n][i];
		}
		gcd= GCD(sum, ans);
		sum/= gcd;
		ans/= gcd;

		if (ans){
			printf("%llu/%llu
", ans, sum);
		}
		else{
			printf("0/1
");
		}
	}

	return 0;
}
原文地址:https://www.cnblogs.com/Idi0t-N3/p/14721530.html