题解 猜拳游戏
首先要求NayC每一轮输赢的概率
这个考场上糊了个贪心DP半天不知道哪里假了
朴素贪心错误的原因是最后要求胜率最大而不是期望得分最大
举个例子:在最后一局NayC落后一分,此时若有 她会选择前面一个,而不在乎可能更低的期望得分
关于力士参孙向神庙的最后一击什么的
所以不能暴力DP
然后这部分的正解是分数规划
令一轮胜率为 ,负率为
要最大化 ,则二分这个值,从后向前DP即可
一个我没想到的初值设定是定义A胜一轮得1分,B胜一轮得mid分(对A来说是-mid分)
- 关于最大化胜率什么的,不能贪心的话是不是都能分数规划啊
然后求出来一轮的胜率,求最终胜率:
- 游戏分每轮进行,每轮二人胜率相等且为给定值
任意时刻如果 比 多赢 轮,则 获胜;如果 比 多赢 轮,则 获胜,求二人胜率:
设 表示 和 获胜轮数差为 时 的胜率,那么有 ,用高斯消元求出 即是答案
或者还有更高端的做法 image
然而我考场上并没有想到
于是我们暴力DP,计算在第 轮胜负的概率
发现可以矩阵乘优化,于是做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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通