結果
| 問題 |
No.425 ジャンケンの必勝法
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 12:59:48 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 2 ms / 2,000 ms |
| コード長 | 6,265 bytes |
| コンパイル時間 | 1,253 ms |
| コンパイル使用メモリ | 100,612 KB |
| 実行使用メモリ | 7,844 KB |
| 最終ジャッジ日時 | 2025-05-14 13:00:54 |
| 合計ジャッジ時間 | 1,780 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 5 |
| other | AC * 18 |
ソースコード
#include <iostream> // For standard input/output operations (cin, cout)
#include <vector> // For using std::vector dynamic arrays
#include <cmath> // For mathematical functions like std::abs, std::max, std::min
#include <iomanip> // For output formatting like std::fixed and std::setprecision
#include <algorithm> // Required for std::max and std::min functions
int main() {
// Optimize standard input/output operations for faster execution
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int P0_percent, Q_percent; // Input variables for initial p (p0) and change q, as percentages
// Read the integer percentage values for p0 and q from standard input
std::cin >> P0_percent >> Q_percent;
// Convert percentage values P0_percent and Q_percent to integer indices/parameters P0 and Q.
// P0 represents the initial probability p after the first draw, as k in k/100.
// Q represents the change in probability p after subsequent draws, as k in k/100.
int P0 = P0_percent;
int Q = Q_percent;
// E[k] will store the probability that Negi eventually wins the game,
// given that the current round follows a draw and Negi's probability
// of using the winning strategy in this round is k/100.
// There are 101 possible states for k, corresponding to percentages 0% through 100%.
std::vector<double> E(101);
// Initialize the probability vector E. A reasonable initial guess can speed up convergence.
// 0.5 is a neutral guess. Initializing with 0.0 also works but might take slightly longer.
for (int k = 0; k <= 100; ++k) {
E[k] = 0.5; // Initial guess for the probability
}
// E_new is used to store the updated values of E in each iteration without interfering
// with calculations within the same iteration that depend on previous E values.
std::vector<double> E_new(101);
// max_diff tracks the maximum absolute change in any E[k] value during an iteration.
// Used to check for convergence. Initialized to a value > tolerance.
double max_diff = 1.0;
int iterations = 0; // Counts the number of iterations performed.
// Set a maximum number of iterations to prevent infinite loops in edge cases (though unlikely here).
const int MAX_ITERATIONS = 10000;
// Set the convergence tolerance. This should be smaller than the required output precision (10^-6).
// Using 1e-12 provides a good margin of safety.
const double CONVERGENCE_TOLERANCE = 1e-12;
// This loop implements the value iteration method to solve the system of linear equations for E_k.
// It continues until the values converge (max_diff <= tolerance) or max iterations are reached.
while (max_diff > CONVERGENCE_TOLERANCE && iterations < MAX_ITERATIONS) {
max_diff = 0.0; // Reset max_diff for the current iteration
// Iterate through all possible states k (representing probability p = k/100)
for (int k = 0; k <= 100; ++k) {
// Determine the indices for the states reached after a draw, depending on strategy choice.
// If Negi used the winning strategy (prob p=k/100) and drew, the next state's probability index is max(0, k-Q).
int prev_k_idx = std::max(0, k - Q);
// If Negi did not use the winning strategy (prob 1-p) and drew, the next state's index is min(100, k+Q).
int next_k_idx = std::min(100, k + Q);
// Apply the derived recurrence relation for E[k]:
// E_k = P(Win this round | p=k/100) + Sum over draw outcomes [ P(Draw outcome) * E[next state] ]
// The simplified equation is:
// E_k = (k+200)/600 + (k/200)*E[prev_k_idx] + ((100-k)/300)*E[next_k_idx]
// Calculate the constant term (related to winning probability in the current round)
// Need to cast k to double for floating point division
E_new[k] = (static_cast<double>(k) + 200.0) / 600.0;
// Add contributions from future states, weighted by the probability of reaching them via a draw
// Term related to using the winning strategy and drawing: (k/100) * (1/2) * E[prev_k_idx] = (k/200) * E[prev_k_idx]
E_new[k] += (static_cast<double>(k) / 200.0) * E[prev_k_idx];
// Term related to not using the winning strategy and drawing: (1 - k/100) * (1/3) * E[next_k_idx] = ((100-k)/300) * E[next_k_idx]
E_new[k] += ( (100.0 - static_cast<double>(k)) / 300.0) * E[next_k_idx];
// Calculate the absolute difference between the new value and the old value for convergence check.
double diff = std::abs(E_new[k] - E[k]);
// Update max_diff if the current difference is the largest seen in this iteration.
if (diff > max_diff) {
max_diff = diff;
}
}
// After calculating all new values, update the E vector for the next iteration.
// std::vector assignment performs a deep copy.
E = E_new;
iterations++; // Increment iteration counter
}
// After the loop finishes (either convergence or max iterations), E[P0] holds the computed probability
// that Negi wins eventually, given that the first draw occurred and the initial probability p was p0 = P0/100.
double final_E_P0 = E[P0];
// Calculate the final answer: The overall probability that Negi wins the game starting from the first round.
// P(Negi wins) = P(Negi wins Round 1) + P(Draw Round 1) * P(Negi wins eventually | Draw Round 1)
// In the first round, both play randomly: P(Win R1) = 1/3, P(Draw R1) = 1/3.
// P(Win eventually | Draw R1) is exactly what E[P0] represents.
// Therefore, P(Negi wins) = 1/3 + (1/3) * E[P0]
double result = 1.0/3.0 + (1.0/3.0) * final_E_P0;
// Output the final computed probability with the required precision.
// std::fixed ensures decimal notation. std::setprecision(15) ensures enough decimal places for the required tolerance.
std::cout << std::fixed << std::setprecision(15) << result << std::endl;
return 0; // Indicate successful execution
}
qwewe