$Luogu$ $P2911$ $[USACO08OCT]$ 牛骨头 $Bovine$ $Bones$

链接

背景

\(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;
}

猜你喜欢

转载自www.cnblogs.com/Peter0701/p/11742020.html