[Sdoi2017]硬幣游戲 [高斯消元 KMP]


[Sdoi2017]硬幣游戲

題意:硬幣序列,H T等概率出現,\(n \le 300\)個人猜了一個長為$ m \le 300$的字符串,出現即獲勝游戲結束。求每個人獲勝概率


考場用了[1444: Jsoi200]有趣的游戲的做法,40分

標解好神!

思想就是用N表示所有沒有人獲勝的狀態,然后列方程

對於A,B兩個串

例如:

A=TTH, B=HTT

那么N+TTH一定會到終止點,但不一定TTH加完后才停止

NTTH = A + BH + BTH

0.125N = A + 0.75B

獲勝概率和為1

n+1個變量 n+1個方程 高斯消元

怎么解釋呢?

就是把一些狀態的概率用一些變量表示出來了呀

計算系數可以兩兩用kmp,求B再方程A中的系數:AB連起來求fail,然后得到后綴=前綴了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=305;
const double eps=1e-10;
inline int read() {
	char c=getchar(); int x=0, f=1;
	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
	return x*f;
}

int n, m;
char s[N][N];
double a[N][N], two[N];
namespace kmp {
	char s[N<<1];
	int fail[N];
	void build(char *s, int n) {
		fail[1] = 0;
		for(int i=2; i<=n; i++) {
			int now = fail[i-1];
			while(now && s[i] != s[now+1]) now = fail[now];
			fail[i] = s[i]==s[now+1] ? now+1 : 0;
		}
	}
	double cal(char *a, char *b) {
		int n = m<<1;
		for(int i=1; i<=m; i++) s[i] = a[i];
		for(int i=1; i<=m; i++) s[i+m] = b[i];
		build(s, n);
		//for(int i=1; i<=n; i++) printf("%d ", fail[i]); puts("");
		int now = fail[n]; double ans = 0;
		while(now) ans += two[m-now], now = fail[now];
		//printf("ans %lf\n", ans);
		return ans;
	}
}

void build() {
	two[0]=1;
	for(int i=1; i<=m; i++) two[i] = two[i-1] * 0.5;

	for(int i=1; i<=n; i++) a[n+1][i] = 1;
	a[n+1][n+2] = 1;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) a[i][j] = kmp::cal(s[i], s[j]);
		a[i][n+1] = -two[m];
	}
}

void gauss(int n) {
	//for(int i=1; i<=n; i++) for(int j=1; j<=n+1; j++) printf("%lf%c", a[i][j], j==n+1 ? '\n' : ' ');
	for(int i=1; i<=n; i++) {
		int r=i;
		for(int k=i; k<=n; k++) if(abs(a[k][i]) > abs(a[r][i])) r=k;
		if(r != i) for(int j=i; j<=n+1; j++) swap(a[r][j], a[i][j]);

		for(int k=i+1; k<=n; k++) {
			double t = a[k][i] / a[i][i];
			for(int j=i; j<=n+1; j++) a[k][j] -= t * a[i][j];
		}
	}
	for(int i=n; i>=1; i--) {
		for(int j=n; j>i; j--) a[i][n+1] -= a[i][j] * a[j][n+1];
		a[i][n+1] /= a[i][i];
	}
}
int main() {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
	n=read(); m=read();
	for(int i=1; i<=n; i++) scanf("%s", s[i]+1);
	build();
	gauss(n+1);
	for(int i=1; i<=n; i++) printf("%.10lf\n", a[i][n+2]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM