背景
\(USACO\) \(2008\) \(Oct\) \(Gold\) \(T1\) , \(Luogu\) \(P2911/BZOJ1599\)
题意
给定三个骰子,分别有 \(s1,s2,s3\) 个面(每个骰子从 \(1\) 开始标号每个面)。求出现次数最多的三骰子数字和。有多个数字和出现次数均为最多时,只要求最小的数字和。
解法
从朴素解法开始:由于 \(s1 \leqslant 20,s2 \leqslant 20,s3 \leqslant 40\) ,故不难想到 \(O(s1s2s3)\) 枚举出所有的情况,开个桶记下所有三骰子数字和的出现次数,找出最大的桶所在的位置即可。
另一种解法
是否有一个数学方法可以 \(O(1)\) 计算答案呢?显然是有的。
不妨手绘出出现次数的分布图像(如下图),
发现最值点在相当长的一段区间内分布。假设 \(s1 \leqslant s2 \leqslant s3\) ,则通过观察或打表容易发现在 \(s1+s2<s3\) 时,最值点区间的左端点(即本题答案)为 \(s1+s2+1\) ;否则答案为 \(\frac{s1+s2+s3+3}{2}\) (根据每个点数的期望计算)。
代码
\(View\) \(Code\)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ret=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret*f;
}
int s1,s2,s3,cnt[85],ans,maxn;
int main()
{
s1=read();
s2=read();
s3=read();
for(register int i=1;i<=s1;i++)
for(register int j=1;j<=s2;j++)
for(register int k=1;k<=s3;k++)
cnt[i+j+k]++;
for(register int i=1;i<=s1+s2+s3;i++)
{
if(maxn<cnt[i])
{
maxn=cnt[i];
ans=i;
}
}
printf("%d\n",ans);
return 0;
}
另一份代码
\(View\) \(Code\)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ret=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret*f;
}
int s1,s2,s3,cnt[85],ans,maxn;
int main()
{
s1=read();
s2=read();
s3=read();
if(s1+s2<s3)
ans=s1+s2+1;
else if(s1+s3<s2)
ans=s1+s3+1;
else if(s2+s3<s1)
ans=s2+s3+1;
else
ans=(s1+s2+s3+3)/2;
printf("%d\n",ans);
return 0;
}