Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js

2016级算法第五次上机-G.ModricWang的撒币游戏

1062 ModricWang的撒币游戏

思路

此题为2017年ACM-ICPC亚洲区域赛乌鲁木齐赛区的A题,现场94个队中有38个队做出此题。在这里作为满分以外的题,是为了让大家看一下外面一些题的风格,不要被三位助教的出题风格所局限。

此题首先需要知道一些高中数学概率论的知识。扔起N个硬币,如果每个硬币下落时,正反面朝上的概率都是确定的,那么这些硬币中正面朝上的数量是呈二项分布的。

考虑使用DP,prob[i][j]prob[i][j] 表示扔了第i次后,有j个硬币正面朝上的概率。首先根据题设,prob[0][0]=1prob[0][0]=1,每次根据ii这一行来推出i+1i+1这一行,扔硬币的过程会让prob[i][j]prob[i][j] 以二项分布分散到prob[i+1][p]prob[i+1][p+k]k+1 项里。j、p和n的关系如下:pjp+kn ,意义为:j表示当前有多少个硬币向上,p表示下一步最少有多少个硬币向上,(p+k)表示下一步最多有多少个硬币向上。所谓的最优策略就是,让p尽量的大,也就是扔硬币的时候尽量选朝下的扔。最终答案就是 Σni=1iprob[m][i]

具体进行操作时可以有一些优化策略,例如

  • 使用滚动数组来节省空间
  • 忽略概率小于1e3 的项

但是我做的时候没有采取这些策略,因为

  • 根据计算,内存空间是足够的
  • 虽然O(Tnmk) 在此题中达到了1e9的数量级,但是计算过程中的核心语句是a+=bc 的形式,这样的语句在X86架构下就是一条FMA指令的事,不必担心常数的问题。 reference: 乘积累加运算 - 维基百科
    and FMA指令集 - 维基百科

时间复杂度:O(Tnmk),空间复杂度O(nm)

代码:


#include <iostream>
#include <iomanip>
#include <cstring>

using std::ios_base;
using std::cin;
using std::cout;
using std::min;
using std::fixed;
using std::setprecision;

const int MaxNum = 100 + 7;

double prob[MaxNum][MaxNum];

double binomial_distribution[MaxNum][MaxNum];

//计算二项分布的概率值
void get_binomial_distribution() {
	double prob_sum = 1;
	for (int i = 1; i < MaxNum; i++) {
		double C = 1;
		prob_sum *= 2;
		binomial_distribution[i][0] = C/prob_sum;
		for (int j = 1; j <= i; j++) {
			C = C*(i - j + 1)/j;
			binomial_distribution[i][j] = C/prob_sum;
		}
	}
}

int main() {
#ifdef ONLINE_JUDGE
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
#else
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif

	get_binomial_distribution();

	int T;
	while (cin >> T) {
		while (T--) {
			int n, m, k;
			cin >> n >> m >> k;
			memset(prob, 0, sizeof(prob));
			prob[0][0] = 1;
			for (int step = 1; step <= m; step++) {
				for (int i = 0; i <= n; i++) {
					int begin_pos = min(i, n - k);	//这就是思路里写的p
					for (int j = 0; j <= k; j++) {
						prob[step][begin_pos + j] += prob[step - 1][i]*binomial_distribution[k][j]; //DP过程
					}
				}
			}
			double ans = 0;
			for (int i = 1; i <= n; i++) {
				ans += i*prob[m][i];
			}
			cout << fixed << setprecision(3) << ans << "\n";
		}
	}
}
posted @   AlvinZH  阅读(489)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
点击右上角即可分享
微信分享提示