抽纸牌
(C++/C)
原题网址:http://www.shnoip.openjudge.cn/datong/08/
总时间限制: 5000ms
单个测试点时间限制: 1000ms
内存限制: 262144kB
描述:
小同迷上了一种奇特的纸牌游戏,这种游戏需要 N 种类型的牌一起,游戏开始时每种牌都需要至少 3 张。
现在小同已经有了一些牌,剩下不够的还需要从纸牌盒中抽取。这是个神奇的纸牌盒,每次抽取牌的时候,都会完全随机地抽出某一种类型的纸牌。也就是说,无论小同手上有什么牌,每种类型的牌都有 1/N 的概率被抽出。
小同想知道,他预计要抽取几次才能够开始游戏,也就是抽取次数的期望。
输入:
第一行一个整数 N(1 ≤ N ≤ 100),表示有 N 种类型的纸牌。
第二行有 N 个整数,依次是 A1, A2,… , AN(0 ≤ Ai ≤ 10),其中 Ai 表示小同已经有多少张 i 类型的牌。
输出:
一个数,表示抽取次数的期望,答案小数部分保留10位有效位数。
样例输入:
样例输入1:
1
0
样例输入2:
2
1 2
样例输入3:
8
3 4 5 6 7 8 9 10
样例输出:
样例输出1:
3.0000000000
样例输出2:
4.5000000000
样例输出3:
0.0000000000
我不会!!我疯了,这什么啊?
CSDN(https://blog.csdn.net)上我也发了:https://blog.csdn.net/2401_86678631/article/details/143807946
为了计算小同需要抽取的预计次数,以确保每种类型的牌至少有3张,我们可以将问题分解为几个步骤。
问题分解
理解需求:
我们需要确保每种类型的牌至少有3张。
对于每种类型 i,如果小同已经有 Ai 张牌,那么还需要的牌数为 max(0, 3 - Ai)。
计算剩余需求:
设 needed[i] = max(0, 3 - Ai) 为类型 i 还需要的牌数。
所有类型牌所需的总数为 total_needed = sum(needed[i] for i in range(N))。
期望抽取次数计算:
由于每种类型的概率都是 1/N,我们需要抽取足够的次数直到获得所需的数量。
要得到特定类型的额外一张牌的期望抽取次数可以用几何分布来建模。
抽到一个特定类型牌的期望次数是 N(因为平均来说需要 1/(1/N) = N 次才能抽到一张该类型的牌)。
总期望抽取次数:
0
N
−
1
needed
[
i
]
×
N
total_expected_draws=
i=0
∑
N−1
needed[i]×N
输出:
最后,我们需要以小数点后10位的格式打印总的期望抽取次数。
实现
以下是实现上述逻辑的Python代码:
python
def expected_draws(N, A):
total_needed = 0
for i in range(N):
needed = max(0, 3 - A[i])
total_needed += needed
# 如果不需要牌,期望抽取次数为0
if total_needed == 0:
return 0.0
# 计算总的期望抽取次数
expected_draws = total_needed * N
return expected_draws
N = int(input().strip())
A = list(map(int, input().strip().split()))
result = expected_draws(N, A)
print(f"{result:.10f}")
代码解释
输入处理:我们读取类型的数量和当前牌的数量。
计算需求:对于每种类型,我们判断还需要多少张牌。
总期望抽取次数:根据需要的牌数计算期望的抽取次数。
输出:最后,我们以小数点后10位的格式打印结果。
示例输出
给定的示例输入:
对于 1 0,输出是 3.0000000000。
对于 2 1 2,输出是 4.5000000000。
对于 8 3 4 5 6 7 8 9 10,输出是 0.0000000000。
这个实现有效地计算了根据当前牌的状态所需的期望抽取次数,并遵循了给定的约束条件。
def expected_draws(N, A):
total_needed = 0
for i in range(N):
needed = max(0, 3 - A[i])
total_needed += needed
# 如果不需要牌,期望抽取次数为0
if total_needed == 0:
return 0.0
# 计算总的期望抽取次数
expected_draws = total_needed * N
return expected_draws
N = int(input().strip())
A = list(map(int, input().strip().split()))
result = expected_draws(N, A)
print(f"{result:.10f}")
是C++的代码,抱歉抱歉,我没说清楚。
我这有个,总是超时,不过你可以参考下
def dp(N, needs):
if sum(needs) == 0:
return 0
total = 0
zero = 0
for i in range(N):
if needs[i] > 0:
needs[i] -= 1
total += dp(N, needs)
needs[i] += 1
else:
zero += 1
#ret = 1 + total/N + zero*ret/N
ret = (N + total)/(N-zero)
#print(needs, ret)
return ret
def expected_draws(N, cards):
needs = [max(3-card,0) for card in cards]
return dp(N, needs)
print(expected_draws(2,[2,1]))
#include <stdio.h>
#include <stdlib.h>
// 递归函数 dp
double dp(int N, int *needs) {
int sum = 0;
for (int i = 0; i < N; i++) {
sum += needs[i];
}
if (sum == 0) {
return 0.0;
}
double total = 0.0;
int zero = 0;
for (int i = 0; i < N; i++) {
if (needs[i] > 0) {
needs[i] -= 1;
total += dp(N, needs);
needs[i] += 1; // 回溯,恢复原状
} else {
zero++;
}
}
double ret = (N + total) / (N - zero);
return ret;
}
// 辅助函数 expected_draws
double expected_draws(int N, int *cards) {
int *needs = (int *)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
needs[i] = (3 - cards[i]) > 0 ? (3 - cards[i]) : 0;
}
double result = dp(N, needs);
free(needs);
return result;
}
int main() {
int N;
scanf("%d", &N);
int *cards = (int *)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
scanf("%d", &cards[i]);
}
double result = expected_draws(N, cards);
printf("%.10f\n", result);
free(cards);
return 0;
}
对于状态Sn,抽一张牌,落在每个位置的概率是想等的1/N
S0 需要 (2,1) = 1 + (S1 + S2)/2 //解方程: S0 = 4.5
S1 需要 (1,1) = 1 + (S3 + S4)/2 //解方程: S1 = 3
S2 需要 (2,0) = 1 + (S3 + S2)/2 //解方程: S2 = 4 // 方程中S2的个数=数组中0的个数
S3 需要 (1,0) = 1 + (S5 + S3)/2 //解方程: S3 = 2
S4 需要 (0,1) = 1 + (S4 + S5)/2 ///解方程 :S4 = 2
S5 需要 (0,0) = 0
本题为OpenJudge - i信息学奥赛 大同杯的题目,目前通过人数0,谁能解出答案,我已经改了60多次代码了,破防了。
– 唐子骞 1个月前