1. 题目描述
有n个球,第i个球的伤害值为$2^i-1, i in [1,n]$。有甲乙两个人,每次由甲选择n个球中的一个,用它以相同概率攻击自己或者乙,同时彻底消耗这个球。这样的攻击最多进行n次。
一旦甲的伤害值高于乙,则甲输,否则甲胜。问甲胜的概率是多少。
2. 基本思路
还是一步步推导。令dp[k]表示共有k个球时甲胜的概率。
egin{align}
dp[1] &= frac{1}{2}
otag \
dp[2] &= frac{1}{2} imes frac{1}{2} imes (1 + dp[1])
otag \
dp[3] &= frac{1}{3} imes frac{1}{2} imes (1 + dp[1] + dp[2])
otag \
dp[4] &= frac{1}{4} imes frac{1}{2} imes (1 + dp[1] + dp[2] + dp[3])
otag \
&cdots
otag \
dp[n] &= frac{1}{n} imes frac{1}{2} imes (1 + Sigma_{i=1}^{n-1}dp[i])
end{align}
为什么上式成立,以$dp[3] = frac{1}{3} imes frac{1}{2} imes (1 + dp[1] + dp[2])$为例解释。
$frac{1}{3} imes frac{1}{2}$表示在第k次取到第3个球的概率(该球一定攻击乙),$k in [1,3]$。
此时,这个球一定属于乙(否则甲必输)并且从此时开始,无论后续的球如何安排,最终都是甲胜。
然而,前k次一定满足甲胜,否则在$[1,k-1]$的某一次中,即停止游戏。
当k=3时,概率为dp[2];
当k=2时,概率为dp[1];
当k=1时,概率为1。
以此类推,dp[n]。
3. 代码
1 import java.lang.*; 2 import java.io.*; 3 import java.util.*; 4 import java.math.BigInteger; 5 6 7 public class Main { 8 9 public static void main(String[] arg) throws java.lang.Exception { 10 InputStream inputStream = System.in; 11 OutputStream outputStream = System.out; 12 InputReader in = new InputReader(inputStream); 13 PrintWriter out = new PrintWriter(outputStream); 14 TaskA solver = new TaskA(); 15 solver.solve(in, out); 16 out.close(); 17 } 18 } 19 20 21 22 class TaskA { 23 public final static int maxn = 505; 24 BigInteger[] FZ = new BigInteger[maxn]; 25 BigInteger[] FM = new BigInteger[maxn]; 26 27 public TaskA() { 28 init(); 29 } 30 31 public void solve(InputReader in, PrintWriter out) { 32 int t = in.nextInt(); 33 int n; 34 35 while (t-- > 0) { 36 n = in.nextInt(); 37 out.println(FZ[n].toString() + "/" + FM[n].toString()); 38 } 39 } 40 41 private void init() { 42 BigInteger sfm = BigInteger.ONE, sfz = BigInteger.ONE; 43 BigInteger fm, fz; 44 BigInteger g, lcm; 45 46 for (int i=1; i<=500; ++i) { 47 fm = sfm.multiply(BigInteger.valueOf(i*2)); 48 fz = sfz; 49 g = fz.gcd(fm); 50 FZ[i] = fz.divide(g); 51 FM[i] = fm.divide(g); 52 // System.out.println(fz + "/" + fm); 53 54 g = sfm.gcd(FM[i]); 55 sfz = sfz.multiply(FM[i].divide(g)) 56 .add( FZ[i].multiply(sfm.divide(g)) ); 57 sfm = FM[i].divide(g).multiply(sfm); 58 } 59 } 60 61 private BigInteger A(int n, int m) { 62 BigInteger ret = BigInteger.ONE; 63 64 for (int i=n; i>n-m; --i) 65 ret = ret.multiply(BigInteger.valueOf(i)); 66 67 return ret; 68 } 69 } 70 71 class InputReader { 72 public BufferedReader reader; 73 public StringTokenizer tokenizer; 74 75 public InputReader(InputStream stream) { 76 reader = new BufferedReader(new InputStreamReader(stream), 32768); 77 tokenizer = null; 78 } 79 80 public String next() { 81 while (tokenizer==null || !tokenizer.hasMoreTokens()) { 82 try { 83 tokenizer = new StringTokenizer(reader.readLine()); 84 } catch (IOException e) { 85 throw new RuntimeException(e); 86 } 87 } 88 return tokenizer.nextToken(); 89 } 90 91 public int nextInt() { 92 return Integer.parseInt(next()); 93 } 94 95 public long nextLong() { 96 return Long.parseLong(next()); 97 } 98 }