在某个游戏中有一个骰子游戏。在游戏中,你需要投掷 5 个标准六面骰子(骰子为一个正方体,6 个面上分别有1、2、3、4、5、6中的一个数字,骰子的质量均匀),投出的点数根据组合会获得一个“获胜等级”。获胜等级从高到低如下:
五个同点数 - 五个骰子显示相同的点数
四个同点数 - 四个骰子显示相同的点数
葫芦 - 一对和一个三个同点数(如1、1、3、3、3)
六高顺子 - 投出的点数为 2、3、4、5、6
五高顺子 - 投出的点数为 1、2、3、4、5
三个同点数 - 三个骰子显示相同的点数(如1、1、1、2、3)
两对 - 投出的点数中有两对是相同的(如 1、1、2、2、3)
一对 - 投出的点数有一对是相同的(如 1、1、2、3、4)
无 - 除去以上的其他情况
给定你已经投出的一次结果,现在假设你可以选择任意个骰子重投一次,请问怎么样操作,才能最大化在重骰后获得更好的获胜等级的概率呢?
注意:更好的获胜等级需要严格地比当前的获胜等级更好,例如 1、1、2、2、3 如果重骰后变为 1、1、3、3、4 并不比当前的获胜等级更好。
输入格式:
输入第一行是一个正整数 T (1≤T≤10),表示接下来有多少组数据。
每组数据只有一行 5 个数字,表示第一次投出的 5 个骰子的点数。
输出格式:
对于每组数据输出三个整数,其中第一个整数为为了获得最大的概率需要重新骰几个骰子,后面的两个整数为重骰骰子后概率的最简分数,其中第二个整数为分子,第三个整数为分母。如果分子为 0,分母为 1。
如果有多种获得最大概率的情况,取重骰的骰子数最少的方案。
输入样例:
3
1 1 2 2 3
1 1 2 3 4
1 1 1 2 3
输出样例:
3 4 9
3 13 18
2 4 9
样例说明:
样例的第一组数据中,一种方案是:重骰最后三个骰子以获得最大的概率(只要重骰的有一个“1”或者三个均相等即可)。
思路
算出所有重新投骰子的方案等级变大的概率取最大。
等级变大的概率=该重新投的方案重新投后等级变大的结果数r[i]/该重新投的方案所有重新投后结果数f[i]
暴力枚举所有重新投后的结果,判断出枚举的每种结果可能由哪些重新投后产生的,来填充f[i]和r[i]
#include <bits/stdc++.h>
using namespace std;
int g[6],h[6];
//f记录每种重投方案(共有32种,5个投与不投)的所有重投的结果树 r记录每种重投方案重投后等级变高的结果数
int f[32],r[32];
int get(int x)
{
int res=0;
while(x)
{
if(x&1) res++;
x>>=1;
}
return res;
}
int getv(int a[])
{
sort(a+1,a+6);
if(a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4]&&a[4]==a[5])
return 9;
if(a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4])
return 8;
if(a[5]==a[4]&&a[2]==a[3]&&a[3]==a[4])
return 8;
if(a[1]==a[2]&&a[2]==a[3]&&a[3]!=a[4]&&a[4]==a[5])
return 7;
if(a[4]==a[5]&&a[4]==a[3]&&a[3]!=a[2]&&a[1]==a[2])
return 7;
if(a[1]==2&&a[2]==3&&a[3]==4&&a[4]==5&&a[5]==6)
return 6;
if(a[1]==1&&a[2]==2&&a[3]==3&&a[4]==4&&a[5]==5)
return 5;
if(a[1]==a[2]&&a[2]==a[3])
return 4;
if(a[3]==a[4]&&a[2]==a[3])
return 4;
if(a[3]==a[4]&&a[4]==a[5])
return 4;
if(a[1]==a[2]&&a[3]==a[4])
return 3;
if(a[1]==a[2]&&a[5]==a[4]) //两对相等的情况不能考虑少
return 3;
if(a[3]==a[2]&&a[5]==a[4])
return 3;
for(int i=2;i<=5;i++){
if(a[i]==a[i-1]) return 2;
}
return 1;
}
int main()
{
int t;cin>>t;
while(t--)
{
memset(f,0,sizeof f);
memset(r,0,sizeof r);
for(int i=1;i<=5;i++) cin>>g[i];
int v1=getv(g);
//5重循环枚举所有重投后的状态,算出每种重投方案的结果数,即f和r
for(int a=1;a<=6;a++){
for(int b=1;b<=6;b++){
for(int c=1;c<=6;c++){
for(int d=1;d<=6;d++){
for(int e=1;e<=6;e++){
h[1]=a,h[2]=b,h[3]=c,h[4]=d,h[5]=e;
//先算出至少哪些地方重投了,因为和原来相等的也可能重投了
int cnt=0;
for(int i=1;i<=5;i++){
if(h[i]!=g[i]) cnt|=(1<<(i-1));
}
int v2=getv(h); //由于这里会把h数组排序,所以要在对比之后进行
//判断该结果是由哪些重投方案造成
//用5位二进制枚举哪几个重投了
for(int i=1;i<32;i++){
//保证数字变的一定重投了,同时数字没变不代表没重投
if((i&cnt)==cnt)
{
f[i]++;
if(v2>v1) r[i]++;
}
}
}
}
}
}
}
int res=0,p=0,q=1;
double ans=-1;
for(int i=1;i<32;i++){
if(r[i]==0) continue;
int c=get(i);
double tmp=(double)r[i]/f[i];
if(tmp>ans||(tmp==ans&&c<res)){
p=r[i],q=f[i];
res=c;
ans=tmp;
}
}
if(p>0&&q>0)
{
int d=__gcd(p,q);
p=p/d,q=q/d;
}
cout<<res<<' '<<p<<' '<<q<<endl;
}
return 0;
}