The Game of Coins
时间限制:10s 内存限制:64MB
问题描述
Cirno和Marisa经常在一起玩抛硬币的游戏,每次由Cirno先选择正面(H)或反面(T),然后抛一次硬币,如果结果和Cirno的选择一致,则Cirno赢,如果结果相反,则Marisa赢。有一天,Marisa对Cirno说,过去的玩法都弱爆了,给Cirno介绍了一种新的玩法。现在Cirno每次选择一个长度为n的由H和T组成的字符串,然后Marisa也选择一个不同的字符串,随后她们不断抛硬币,直到其中一个人选择的字符串和最后n次抛硬币的结果相匹配,匹配的人将获胜。玩了一段时间后,Cirno发现自己的胜率下降了,但又不知道为什么。现在,Cirno想知道在给定自己和Marisa所选的字符串时,自己获胜的概率。
输入
第一行为T,表示输入数据组数。
下面T组数据。每组数据都是两个空格分割的,长度均为n的,由H和T组成的字符串。
输出
对第i组数据,输出
Case #i:
然后输出Cirno获胜的概率,并以最简分数形式输出。
限制条件
1<=T<=500
1<=n<=9+9+9
样例输入
3
H T
HT TH
HH TH
样例输出
Case #1:
1/2
Case #2:
1/2
Case #3:
1/4
解题报告:
# input1/output1: n <=3
方法比较多,可以用和input2/outpu2类似的方法,以2^n为状态。
也可以用Monte Carlo求出浮点数解,反过来得到精确解(可以假设分母都不会太大)。
还可以利用对称性分类,再人肉求解后打表(有不少是1/2)。
# input2/output2: n <= 27
以输入的字符串a和b的所有后缀作为状态,每抛一次硬币,就有一半的概率转移到两个新的状态。
状态a和b本身是终止状态,对应的胜率分别为1和2。
p_a = 1
p_b = 0
而对于其余状态i,则有
p_i = 0.5 * p_{s(i+H)} + 0.5 * p_{s(i+T)}
于是可以得到一个n元线性方程组,用Gauss消元法可解。
# input3/output3: n <= 729
算法参考solution3.rb。
记k_{ij}=correlation(i, j), k_i=k_{ii}-k_{ij}
a胜利的概率是k_b/(k_a+k_b)
b胜利的概率是k_a/(k_a+k_b)
参考:String overlaps, pattern matching, and nontransitive games。
解题代码:
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long llint;
llint gcd(llint a, llint b) {
return b == 0 ? a : gcd(b, a % b);
}
struct Fraction {
llint num, den;
Fraction(llint n = 0, llint d = 1): num(n), den(d) {
llint g = gcd(num, den);
num /= g;
den /= g;
if (den < 0) {
num = -num;
den = -den;
}
}
};
Fraction operator+(const Fraction& lhs, const Fraction& rhs) {
llint g = gcd(lhs.den, rhs.den);
return Fraction(lhs.num * (rhs.den / g) + rhs.num * (lhs.den / g), lhs.den / g * rhs.den);
}
Fraction operator-(const Fraction& lhs, const Fraction& rhs) {
return lhs + Fraction(-rhs.num, rhs.den);
}
Fraction operator*(const Fraction& lhs, const Fraction& rhs) {
llint g1 = gcd(lhs.num, rhs.den);
llint g2 = gcd(rhs.num, lhs.den);
return Fraction((lhs.num / g1) * (rhs.num / g2), (lhs.den / g2) * (rhs.den / g1));
}
Fraction operator/(const Fraction& lhs, const Fraction& rhs) {
return lhs * Fraction(rhs.den, rhs.num);
}
Fraction operator==(const Fraction& lhs, const Fraction& rhs) {
return lhs.num == rhs.num && lhs.den == rhs.den;
}
const int MAXN = 64;
void LU(int n, Fraction a[MAXN][MAXN], int r[MAXN], int c[MAXN]) {
for (int i = 0; i < n; ++i) {
r[i] = c[i] = i;
}
for (int k = 0; k < n; ++k) {
int ii = -1, jj = -1;
for (int i = k; ii == -1 && i < n; ++i) {
for (int j = k; jj == -1 && j < n; ++j) {
if (a[i][j].num != 0) {
ii = i;
jj = j;
}
}
}
swap(r[k], r[ii]);
swap(c[k], c[jj]);
for (int i = 0; i < n; ++i) {
swap(a[i][k], a[i][jj]);
}
for (int j = 0; j < n; ++j) {
swap(a[k][j], a[ii][j]);
}
for (int i = k + 1; i < n; ++i) {
a[i][k] = a[i][k] / a[k][k];
for (int j = k + 1; j < n; ++j) {
a[i][j] = a[i][j] - a[i][k] * a[k][j];
}
}
}
}
void solve(int n, Fraction a[MAXN][MAXN], int r[MAXN], int c[MAXN], Fraction b[MAXN]) {
static Fraction x[MAXN];
for (int i = 0; i < n; ++i) {
x[i] = b[r[i]];
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
x[i] = x[i] - a[i][j] * x[j];
}
}
for (int i = n - 1; i >= 0; --i) {
for (int j = n - 1; j > i; --j) {
x[i] = x[i] - a[i][j] * x[j];
}
x[i] = x[i] / a[i][i];
}
for (int i = 0; i < n; ++i) {
b[c[i]] = x[i];
}
}
Fraction a[MAXN][MAXN];
int r[MAXN];
int c[MAXN];
Fraction b[MAXN];
int main() {
int n, m;
string x, y, z;
vector<string> v;
int TT, NN;
cin >> NN;
for (TT = 1; TT <= NN; TT++) {
cin >> x >> y;
v.clear();
n = x.length();
for (int i = 0; i <= n; ++i) {
v.push_back(x.substr(0, i));
v.push_back(y.substr(0, i));
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
m = v.size();
for (int i = 0; i < m; ++i) {
fill(a[i], a[i] + m, 0);
a[i][i] = 1;
b[i] = v[i] == x ? 1 : 0;
if (v[i] == x || v[i] == y) {
continue;
}
const char char_list[2] = {'H', 'T'};
for (int list_idx = 0; list_idx < 2; list_idx ++) {
char c = char_list[list_idx];
//for (char c: {'H', 'T'}) {
z = v[i] + c;
while (find(v.begin(), v.end(), z) == v.end()) {
z = z.substr(1);
}
int j = find(v.begin(), v.end(), z) - v.begin();
a[i][j] = a[i][j] - Fraction(1, 2);
}
}
LU(m, a, r, c);
solve(m, a, r, c, b);
cout<<"Case #"<<TT<<":"<<endl;
cout<<b[0].num<<"/"<<b[0].den<<endl;
// printf("Case #%d:\n", TT);
// printf("%lld/%lld\n", b[0].num, b[0].den);
}
return 0;
}