抽牌

1 题目描述

https://www.nowcoder.com/practice/8b8c4fc44b614862b2a4f53771577995?tpId=90&tqId=30855&tPage=1&rp=1&ru=/ta/2018test&qru=/ta/2018test/question-ranking

2 递归解法

轮到甲抽牌的时候,下一轮再轮到甲抽牌的时候,四种可能:

  1. 甲上乙上
  2. 甲上乙下
  3. 甲下乙上
  4. 甲下乙下

其实在遍历一颗四叉树,树的高度log2(n),复杂度应该是2^n。

2.1 实例

输入:
5 10 20
1 3 4 5 13

递归过程就是:遍历一颗四叉树

2.2 代码实现

int* array;
double p;
double q;
double g_score;
//四叉树
void run(int begin, int end, int sum/*甲已有的牌的和*/, double Probability/*已累计的概率*/)
{
	if (end - begin <= 1) {
		g_score += (sum + array[begin]) * Probability* p;//甲上
		g_score += (sum + array[end]) * Probability* (1 - p);//甲下
		return;
	}
	//甲上乙上
	run(begin + 2, end, sum + array[begin], Probability* p * q);
	//甲上乙下
	run(begin + 1, end - 1, sum + array[begin], Probability* p * (1 - q));
	//甲下乙上
	run(begin + 1, end - 1, sum + array[end], Probability* (1 - p) * q);
	//甲下乙下
	run(begin, end - 2, sum + array[end], Probability* (1 - p) * (1 - q));
}
int main()
{
	int n;
	int pint, qint;
	cin >> n >> pint >> qint;
	p = (double)pint / 100.0;
	q = (double)qint / 100.0;
	array = (int*)malloc(sizeof(int)* n);
	for (int i = 0; i<n; i++) {
		cin >> array[i];
	}
	run(0, n - 1, 0, 1);
	printf("%.3f\n", g_score);
	return 0;
}

2.3 问题

复杂度太大,O(2^n),指数级。

3动态规划解法

3.1 子问题界定和计算顺序

子问题
F(begin , end):当牌只有array[ begin ], array[begin+1], … array[ end ],甲的得分期望。
如何计算F(begin,end)呢?划分成四个子问题:

  1. p * q * (array[begin] + F[begin + 2][end]) //甲上乙上
  2. p * (1 - q) * (array[begin] + F[begin + 1][end - 1]) //甲上乙下
  3. (1 - p) * q * (array[end] + F[begin + 1][end - 1]) //甲下乙上
  4. (1 - p) * (1 - q) * (array[end] + F[begin][end - 2]) //甲下乙下
    将这四种可能相加,即可。

计算顺序
见下面的实例

3.2 实例

输入:
5 10 20
1 3 4 5 13

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.1 计算顺序

在这里插入图片描述

3.3 代码实现

#include <iostream>
#include <algorithm>

using namespace std;

double** dp;
int* array;
double p;
double q;

void func(int begin, int end)
{
	if (begin == end) {//一个元素
		dp[begin][end] = array[begin];
		return;
	}
	else if (end - begin == 1) {//两个元素
		dp[begin][end] = p * array[begin] + (1 - p) * array[end];
		return;
	}
	//多个元素
	dp[begin][end] = 
		p*q*(array[begin] + dp[begin + 2][end]) + //甲上乙上
		p*(1 - q)*(array[begin] + dp[begin + 1][end - 1]) + //甲上乙下
		(1 - p)*q*(array[end] + dp[begin + 1][end - 1]) + //甲下乙上
		(1 - p)*(1 - q)*(array[end] + dp[begin][end - 2]); //甲下乙下
}


int main()
{
	int n;
	int pint, qint;
	cin >> n >> pint >> qint;
	p = (double)pint / 100.0;
	q = (double)qint / 100.0;
	//分配结果dp
	dp = (double**)malloc(sizeof(double*)* n);
	for (int i = 0; i < n; i++) {
		dp[i] = (double*)malloc(sizeof(double)* n);
	}
	//分配数组
	array = (int*)malloc(sizeof(int)* n);
	for (int i = 0; i<n; i++) {
		cin >> array[i];
	}
	//注意计算顺序
	for (int gap = 0; gap < n; gap++) {
		for (int i = 0; i < n; i++) {
			int j = i + gap;
			if (j < n) {
				func(i, j);
			} else {
				break;
			}
		}
	}
	printf("%.3f", dp[0][n - 1]);
	return 0;
}

3.4 复杂度

只需要计算表格的一半即可,O(1/2 * n^2)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值