InternBootcamp/internbootcamp/bootcamp/cneedforpinkslips/cneedforpinkslips.py
2025-05-23 15:27:15 +08:00

301 lines
12 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""#
### 谜题描述
After defeating a Blacklist Rival, you get a chance to draw 1 reward slip out of x hidden valid slips. Initially, x=3 and these hidden valid slips are Cash Slip, Impound Strike Release Marker and Pink Slip of Rival's Car. Initially, the probability of drawing these in a random guess are c, m, and p, respectively. There is also a volatility factor v. You can play any number of Rival Races as long as you don't draw a Pink Slip. Assume that you win each race and get a chance to draw a reward slip. In each draw, you draw one of the x valid items with their respective probabilities. Suppose you draw a particular item and its probability of drawing before the draw was a. Then,
* If the item was a Pink Slip, the quest is over, and you will not play any more races.
* Otherwise,
1. If a≤ v, the probability of the item drawn becomes 0 and the item is no longer a valid item for all the further draws, reducing x by 1. Moreover, the reduced probability a is distributed equally among the other remaining valid items.
2. If a > v, the probability of the item drawn reduces by v and the reduced probability is distributed equally among the other valid items.
For example,
* If (c,m,p)=(0.2,0.1,0.7) and v=0.1, after drawing Cash, the new probabilities will be (0.1,0.15,0.75).
* If (c,m,p)=(0.1,0.2,0.7) and v=0.2, after drawing Cash, the new probabilities will be (Invalid,0.25,0.75).
* If (c,m,p)=(0.2,Invalid,0.8) and v=0.1, after drawing Cash, the new probabilities will be (0.1,Invalid,0.9).
* If (c,m,p)=(0.1,Invalid,0.9) and v=0.2, after drawing Cash, the new probabilities will be (Invalid,Invalid,1.0).
You need the cars of Rivals. So, you need to find the expected number of races that you must play in order to draw a pink slip.
Input
The first line of input contains a single integer t (1≤ t≤ 10) — the number of test cases.
The first and the only line of each test case contains four real numbers c, m, p and v (0 < c,m,p < 1, c+m+p=1, 0.1≤ v≤ 0.9).
Additionally, it is guaranteed that each of c, m, p and v have at most 4 decimal places.
Output
For each test case, output a single line containing a single real number — the expected number of races that you must play in order to draw a Pink Slip.
Your answer is considered correct if its absolute or relative error does not exceed 10^{-6}.
Formally, let your answer be a, and the jury's answer be b. Your answer is accepted if and only if \frac{|a - b|}{max{(1, |b|)}} ≤ 10^{-6}.
Example
Input
4
0.2 0.2 0.6 0.2
0.4 0.2 0.4 0.8
0.4998 0.4998 0.0004 0.1666
0.3125 0.6561 0.0314 0.2048
Output
1.532000000000
1.860000000000
5.005050776521
4.260163673896
Note
For the first test case, the possible drawing sequences are:
* P with a probability of 0.6;
* CP with a probability of 0.2⋅ 0.7 = 0.14;
* CMP with a probability of 0.2⋅ 0.3⋅ 0.9 = 0.054;
* CMMP with a probability of 0.2⋅ 0.3⋅ 0.1⋅ 1 = 0.006;
* MP with a probability of 0.2⋅ 0.7 = 0.14;
* MCP with a probability of 0.2⋅ 0.3⋅ 0.9 = 0.054;
* MCCP with a probability of 0.2⋅ 0.3⋅ 0.1⋅ 1 = 0.006.
So, the expected number of races is equal to 1⋅ 0.6 + 2⋅ 0.14 + 3⋅ 0.054 + 4⋅ 0.006 + 2⋅ 0.14 + 3⋅ 0.054 + 4⋅ 0.006 = 1.532.
For the second test case, the possible drawing sequences are:
* P with a probability of 0.4;
* CP with a probability of 0.4⋅ 0.6 = 0.24;
* CMP with a probability of 0.4⋅ 0.4⋅ 1 = 0.16;
* MP with a probability of 0.2⋅ 0.5 = 0.1;
* MCP with a probability of 0.2⋅ 0.5⋅ 1 = 0.1.
So, the expected number of races is equal to 1⋅ 0.4 + 2⋅ 0.24 + 3⋅ 0.16 + 2⋅ 0.1 + 3⋅ 0.1 = 1.86.
Here is a reference code to solve this task. You can use this to help you genereate cases or validate the solution.
```python
PREC = 0.00000005
def floatEq(a, b):
return abs(a-b) < PREC
def f(c, m, p, v, n):
r = (n+1)*p
if c is not None and (c < v or floatEq(c, v)):
if m is not None:
r += c*f(None, m+c/2.0, p+c/2.0, v, n+1)
else:
r += c*f(None, None, p+c, v, n+1)
elif c is not None and c > v:
if m is not None:
r += c*f(c-v, m+v/2.0, p+v/2.0, v, n+1)
else:
r += c*f(c-v, None, p+v, v, n+1)
if m is not None and (m < v or floatEq(m, v)):
if c is not None:
r += m*f(c+m/2.0, None, p+m/2.0, v, n+1)
else:
r += m*f(None, None, p+m, v, n+1)
elif m is not None and m > v:
if c is not None:
r += m*f(c+v/2.0, m-v, p+v/2.0, v, n+1)
else:
r += m*f(None, m-v, p+v, v, n+1)
return r
I=lambda: map(float, raw_input().split())
t = input()
for _ in xrange(t):
c, m, p, v = I()
print f(c, m, p, v, 0)
```
请完成上述谜题的训练场环境类实现,包括所有必要的方法。
"""
from bootcamp import Basebootcamp
import random
import re
from bootcamp import Basebootcamp
class Cneedforpinkslipsbootcamp(Basebootcamp):
def __init__(self, **params):
self.params = params
super().__init__(**params)
def case_generator(self):
# 随机选择生成类型普通案例、含极值p、初始值触发无效条件
case_type = random.choice(['normal', 'extreme_p', 'low_prob'])
if case_type == 'normal':
# 标准生成方式
a = random.randint(1, 9998)
max_b = 10000 - a - 1 # 保证至少留1给d
b = random.randint(1, max_b)
d = 10000 - a - b
c = round(a / 10000, 4)
m = round(b / 10000, 4)
p = round(d / 10000, 4)
v = round(random.randint(1000, 9000) / 10000, 4)
elif case_type == 'extreme_p':
# 生成极小的p值类似第三个测试案例
p = round(random.choice([0.0001, 0.0002, 0.0003, 0.0004]), 4)
remaining = round(1 - p, 4)
c = round(remaining * random.uniform(0.4, 0.6), 4)
m = round(remaining - c, 4)
v = round(random.uniform(0.15, 0.25), 4)
elif case_type == 'low_prob':
# 生成会立即触发无效条件的案例
v_val = round(random.uniform(0.1, 0.3), 4)
target = random.choice(['c', 'm'])
if target == 'c':
c = round(random.uniform(0.01, v_val), 4)
remaining = 1 - c
m = round(remaining * random.uniform(0.3, 0.7), 4)
p = round(remaining - m, 4)
else:
m = round(random.uniform(0.01, v_val), 4)
remaining = 1 - m
c = round(remaining * random.uniform(0.3, 0.7), 4)
p = round(remaining - c, 4)
v = v_val
# 校验并修正浮点精度
total = round(c + m + p, 4)
if total != 1.0:
p = round(1 - c - m, 4)
return {
'c': c,
'm': m,
'p': p,
'v': v
}
@staticmethod
def prompt_func(question_case):
c = question_case['c']
m = question_case['m']
p = question_case['p']
v = question_case['v']
prompt = f"""你刚刚击败了一个黑名单对手获得了一个抽取奖励纸条的机会。初始时有三个有效纸条现金纸条Cash Slip初始概率为{c:.4f}、扣押解除标记Impound Strike Release Marker初始概率为{m:.4f}和对手车辆的粉色纸条Pink Slip初始概率为{p:.4f})。每次比赛获胜后,你可以抽取一张纸条。只要未抽到粉色纸条,你就可以继续参赛。每次抽取后,根据以下规则调整剩余纸条的概率:
1. 如果抽到的是粉色纸条,游戏结束,不再进行任何比赛。
2. 如果抽到的是其他纸条(如现金或解除标记):
- 若该纸条当前的抽取概率a小于等于波动因子v当前v={v:.4f}则该纸条变为无效概率变为0其原有概率平均分配给剩余的合法纸条。
- 若该纸条的概率a大于v则其概率减少v减少的部分平均分配给其他合法纸条。
你的任务是计算需要进行的比赛的期望次数(即抽到粉色纸条之前的总抽取次数)。
输入数据为四个实数:{c:.4f} {m:.4f} {p:.4f} {v:.4f}。请严格根据上述规则计算期望值并确保答案的绝对或相对误差不超过1e-6。答案必须包含足够多的小数位例如类似1.532000000000的格式)。
请将计算出的期望值放在[answer]和[/answer]的标签内。例如:[answer]1.532000000000[/answer]。"""
return prompt
@staticmethod
def extract_output(output):
matches = re.findall(r'\[answer\]\s*([\d.]+)\s*\[/answer\]', output, re.IGNORECASE)
if not matches:
return None
try:
return float(matches[-1].strip())
except:
return None
@classmethod
def _verify_correction(cls, solution, identity):
expected = cls._calculate_expected(identity['c'], identity['m'], identity['p'], identity['v'])
if solution is None:
return False
try:
solution_float = float(solution)
except:
return False
# 双重验证机制
abs_error = abs(solution_float - expected)
if abs_error < 1e-6:
return True
rel_error = abs_error / max(1.0, abs(expected))
return rel_error <= 1e-6
@staticmethod
def _calculate_expected(c_val, m_val, p_val, v_val):
PREC = 1e-8
def float_eq(a, b):
return abs(a - b) < PREC
memo = {}
def f(c, m, p, n):
key = (c if c is None else round(c,8),
m if m is None else round(m,8),
n)
if key in memo:
return memo[key]
total = (n+1)*p
remaining = []
if c is not None:
remaining.append(('c', c))
if m is not None:
remaining.append(('m', m))
for item, value in remaining:
if item == 'c':
current_c = value
if current_c < v_val or float_eq(current_c, v_val):
# Case 1: becomes invalid
if m is not None:
delta = current_c / 2
contribution = current_c * f(None, m+delta, p+delta, n+1)
else:
contribution = current_c * f(None, None, p+current_c, n+1)
else:
# Case 2: reduce by v
new_c = current_c - v_val
if m is not None:
delta = v_val / 2
contribution = current_c * f(new_c, m+delta, p+delta, n+1)
else:
contribution = current_c * f(new_c, None, p+v_val, n+1)
total += contribution
elif item == 'm':
current_m = value
if current_m < v_val or float_eq(current_m, v_val):
# Case 1: becomes invalid
if c is not None:
delta = current_m / 2
contribution = current_m * f(c+delta, None, p+delta, n+1)
else:
contribution = current_m * f(None, None, p+current_m, n+1)
else:
# Case 2: reduce by v
new_m = current_m - v_val
if c is not None:
delta = v_val / 2
contribution = current_m * f(c+delta, new_m, p+delta, n+1)
else:
contribution = current_m * f(None, new_m, p+v_val, n+1)
total += contribution
memo[key] = total
return total
c = c_val if c_val > 1e-8 else None
m = m_val if m_val > 1e-8 else None
p = p_val
return f(c, m, p, 0)