UVA1335/洛谷P4409 皇帝的烦恼 题解

题目链接

双倍经验

看题解里面好像还没有乱搞的做法,于是我就来写一篇乱搞做法。

题意

\(n\) 个人围坐成一个圈分多种礼物,每个人可以领到 \(a_i\) 种礼物,要保证相邻两个人的礼物不能有一件相同,求礼物种类数的最小值。

思路

首先考虑破环成链,令 \(a_{n+1}=a_1\),将信息转化为长度为 \(n+1\) 的数组 \(a\)

有一种很显然的乱搞求法,直接遍历每个 \(a_i\),维护 \(\max\left\{a_i+a_{i+1}\right\}\)。这种做法看似正确,但是显然能随便找出 hack 数据:

in

3
5
5
5
0

wrong ans

10

ans

15

既然这种方法出 bug 了,那么我们可以考虑为什么会出问题,打上补丁就好了。

首先想想为什么这么做,有什么实际意义?

遍历 \(a_i\),其实就是相当于每次只考虑第 \(i\) 个人能看到多少种不同的礼物。我们发现,他只能看到第 \(i-1\) 个人和第 \(i+1\) 个人的礼物,如果要满足他看到的与自己的礼物都不同,那么至少有 \(a_i+\max\left\{a_{i-1},a_{i+1}\right\}\) 种礼物。

而得到这个答案也有一个前提:假设第 \(i\) 个人的左边的人和右边的人礼物的总种数等于两个人中礼物更多的那个人的礼物种数,换句话说,对于这两个人中礼物少的那个人的每一种礼物,都能在礼物更多的那个人那里找到。

而每次考虑下一个人的时候,我们会重新进行假设,不会顾及是否会与前一个人的假设产生矛盾。也就是说,我们每进行一次假设,都是独立地考虑问题的,并没有考虑会不会与先前的假设产生矛盾,因此可能真正的答案会比这种方法所求的答案更大。

分析完问题,我们考虑给这种方法打上补丁。

宏观的看看这个问题,假设一共有 \(sum\) 个礼物,则有 \(sum=\sum_{i=1}^na_i\),总共有 \(n\) 个人,那么每种礼物有最多 \(\left\lfloor\frac{n}{2}\right\rfloor\) 个人能拿到,因此总共就有 \(\left\lceil\frac{sum}{\left\lfloor\frac{n}{2}\right\rfloor}\right\rceil\) 种礼物。

所以最终答案就是 \(\max\left\{\max\left\{a_i+a_{i+1}\right\},\left\lceil\frac{sum}{\left\lfloor\frac{n}{2}\right\rfloor}\right\rceil \right\}\)。补丁下载完毕。

由于本人能力有限,不保证能解答清楚关于这种方法的证明,因此在此就不讨论这种方法的正确性啦。具体的证明可以参考一下这篇题解

一些小细节

  • 破环成链的时候需要特判一下 \(n>1\),因为 \(n=1\) 时根本不存在环。

  • 答案会爆 int,因此记得开 long long。

  • 不要忘记每次清空数组。

  • 记得强制类型转换。

参考代码

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n;
ll a[N];
int main()
{
    // freopen("test.in","r",stdin);
    // freopen("myans","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    while(cin>>n,n)
    {
        memset(a,0,sizeof a);
        ll ans=0,sum=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            sum+=a[i];
        }
        if(n>1)a[n+1]=a[1];
        for(int i=1;i<=n;i++)ans=max(ans,a[i]+a[i+1]);
        ans=max(ans,(ll)ceil((double)sum/(n/2)));
        cout<<ans<<"\n";
    }
    return 0;
}
posted @ 2023-07-14 12:11  week_end  阅读(18)  评论(0编辑  收藏  举报