题解 猜拳游戏

传送门

首先要求NayC每一轮输赢的概率
这个考场上糊了个贪心DP半天不知道哪里假了
朴素贪心错误的原因是最后要求胜率最大而不是期望得分最大
举个例子:在最后一局NayC落后一分,此时若有 \((0.5, 0.5), (0.4, 0.2)\) 她会选择前面一个,而不在乎可能更低的期望得分
关于力士参孙向神庙的最后一击什么的
所以不能暴力DP
然后这部分的正解是分数规划
令一轮胜率为 \(p\),负率为 \(r\)
要最大化 \(\frac{p}{p+r}\),则二分这个值,从后向前DP即可
一个我没想到的初值设定是定义A胜一轮得1分,B胜一轮得mid分(对A来说是-mid分)

  • 关于最大化胜率什么的,不能贪心的话是不是都能分数规划啊

然后求出来一轮的胜率,求最终胜率:

  • 游戏分每轮进行,每轮二人胜率相等且为给定值
    任意时刻如果 \(A\)\(B\) 多赢 \(m_1\) 轮,则 \(A\) 获胜;如果 \(B\)\(A\) 多赢 \(m_2\) 轮,则 \(B\) 获胜,求二人胜率:
    \(a[i]\) 表示 \(A\)\(B\) 获胜轮数差为 \(i\)\(A\) 的胜率,那么有 \(a[m_1]=1,a[-m_2]=0\),用高斯消元求出 \(a[0]\) 即是答案
    或者还有更高端的做法 image

然而我考场上并没有想到
image
于是我们暴力DP,计算在第 \(i\) 轮胜负的概率
发现可以矩阵乘优化,于是做1e9次就可以了,精度误差可以忽略不计

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define double long double
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m1, m2;
const double eps=1e-6;
double dp[1010][2010], win[N], lose[N];
double r[N], p[N], s[N];
int dlt, dlt2, bkp;
struct matrix{
	int n, m;
	double a[210][210];
	matrix(){}
	matrix(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
	inline void resize(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
	inline double* operator [] (int t) {return a[t];}
	inline matrix operator * (matrix b) {
		matrix ans(n, b.m);
		for (int i=1; i<=n; ++i)
			for (int k=1; k<=b.m; ++k)
				for (int j=1; j<=m; ++j)
					ans[i][k]=ans[i][k]+a[i][j]*b[j][k];
		return ans;
	}
}mat, tr;
inline matrix qpow(matrix a, ll b) {matrix ans=a; --b; for (; b; a=a*a,b>>=1) if (b&1) ans=ans*a; return ans;}
double query(double add, double del) {
	mat.resize(1, m1+m2+2); tr.resize(m1+m2+2, m1+m2+2);
	dlt2=m2+1; bkp=m1+1;
	mat[1][0+dlt2]=1;
	tr[m1+dlt2][bkp+dlt2]=1; tr[bkp+dlt2][bkp+dlt2]=1;
	for (int i=-m2+1; i<m1; ++i) tr[i+dlt2][i+1+dlt2]=add, tr[i+dlt2][i-1+dlt2]=del;
	mat=mat*qpow(tr, 1e9);
	return mat[1][bkp+dlt2];
}

namespace force{
	struct sit{int fir, sec; sit(){} sit(int a, int b):fir(a),sec(b){}}sta[1010][5];
	inline bool operator < (sit a, sit b) {return a.fir-a.sec > b.fir-b.sec;}
	void solve() {
		for (int i=1; i<=n; ++i) {
			sta[i][1]=sit(s[i], p[i]);
			sta[i][2]=sit(r[i], s[i]);
			sta[i][3]=sit(p[i], r[i]);
			sort(sta[i]+1, sta[i]+4);
			// cout<<"sta: "; for (int j=1; j<=3; ++j) cout<<"("<<sta[i][j].fir<<','<<sta[i][j].sec<<")"<<' '; cout<<endl;
			win[i]=sta[i][1].fir; win[i]/=100;
			lose[i]=sta[i][1].sec; lose[i]/=100;
		}
		// cout<<"win: "; for (int i=1; i<=n; ++i) cout<<win[i]<<' '; cout<<endl;
		// cout<<"lose: "; for (int i=1; i<=n; ++i) cout<<lose[i]<<' '; cout<<endl;
		dlt=n+1;
		memset(dp, 0, sizeof(dp));
		dp[0][dlt]=1;
		for (int i=1; i<=n; ++i)
			for (int j=-i; j<=i; ++j)
				dp[i][j+dlt]=win[i]*dp[i-1][j-1+dlt] + lose[i]*dp[i-1][j+1+dlt] + (1.0-win[i]-lose[i])*dp[i-1][j+dlt];
		// cout<<"dp: "; for (int j=-n; j<=n; ++j) cout<<dp[n][j+dlt]<<' '; cout<<endl;
		// double sum=0;
		// for (int j=-n; j<=n; ++j) sum+=dp[n][j+dlt];
		// printf("sum: %.6lf\n", sum);
		double add=0, del=0, tem;
		for (int j=1; j<=n; ++j) add+=dp[n][j+dlt];
		for (int j=-n; j<0; ++j) del+=dp[n][j+dlt];
		if (fabs(add)<eps&&fabs(del)<eps) {puts("0.00000"); return ;}
		// cout<<"add&del: "<<add<<' '<<del<<endl;
		tem=add+del;
		add=add/tem, del=del/tem;
		// cout<<"add&del: "<<add<<' '<<del<<endl;
		printf("%.5lf\n", query(add, del));
	}
}

namespace task{
	double dp[1010][2010], tem[10];
	bool check(double t) {
		dp[n][0+dlt]=0;
		for (int i=1; i<=n; ++i) dp[n][i+dlt]=1;
		for (int i=-n; i<0; ++i) dp[n][i+dlt]=-t;
		for (int i=n-1; ~i; --i) {
			for (int j=-i; j<=i; ++j) {
				tem[1]=s[i+1]*dp[i+1][j+1+dlt]+p[i+1]*dp[i+1][j-1+dlt]+r[i+1]*dp[i+1][j+dlt];
				tem[2]=r[i+1]*dp[i+1][j+1+dlt]+s[i+1]*dp[i+1][j-1+dlt]+p[i+1]*dp[i+1][j+dlt];
				tem[3]=p[i+1]*dp[i+1][j+1+dlt]+r[i+1]*dp[i+1][j-1+dlt]+s[i+1]*dp[i+1][j+dlt];
				sort(tem+1, tem+4);
				dp[i][j+dlt]=tem[3];
			}
		}
		return dp[0][0+dlt]>0;
	}
	void solve() {
		dlt=n+1;
		for (int i=1; i<=n; ++i) r[i]/=100, p[i]/=100, s[i]/=100;
		double l=0, r=1000000, mid;
		for (int t=1; t<=50; ++t) {
			mid=(l+r)/2;
			if (check(mid)) l=mid;
			else r=mid;
		}
		// cout<<"mid: "<<mid<<endl;
		double add=1.0-1.0/(1.0+mid), del=1-add;
		cerr<<"add&del: "<<add<<' '<<del<<endl;
		printf("%.5lf\n", query(add, del));
	}
}

signed main()
{
	while (1) {
		n=read(); m1=read(); m2=read();
		bool any_no_zero=0;
		if (!(n||m1||m2)) break;
		for (int i=1; i<=n; ++i) {
			r[i]=read(); p[i]=read(); s[i]=read();
			if (r[i]>0&&p[i]>0&&s[i]>0) any_no_zero=1;
		}
		// force::solve();
		if (!any_no_zero) puts("1.00000");
		else task::solve();
	}
	
	return 0;
}
posted @ 2022-01-06 21:32  Administrator-09  阅读(0)  评论(0编辑  收藏  举报