糖果传递(环形均分纸牌和链式均分纸牌)

原题链接:糖果传递


前言

糖果传递----其实就是一种环形均分纸牌问题,在此之前我们需要先了解,链式均分纸牌。

一、链式均分纸牌

题目描述

有N堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N−1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

思路:

第一堆牌相差的牌只能由第二堆牌承担(给予或索要)
第一堆牌都达到要求了又去动它干嘛
可以直接删除第一堆牌
(因为
1.已经完成目标
2.无论怎么移动都只能从第二堆里找
所以不要去管它)
第二堆牌神奇的变成了第一堆牌
重复上述思想即可解决本题。

代码实现:

#include <bits/stdc++.h>
using namespace std;
int n,avg,ans;
int a[105];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){cin>>a[i];avg+=a[i];}
    avg/=n;
    for(int i=1;i<=n;i++)
    {
        if(a[i]-avg)
        {
            a[i+1]+=a[i]-avg;
            ans++;
        }
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

二、糖果传递(环形纸牌)

题目描述

有 n 个小朋友坐成一圈,每人有 a[i]个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。求使所有人获得均等糖果的最小代价。

思路:

题目描述其实就是在说一个环里面糖怎么去传递,让每个人最后获得一样的糖且传递次数最少。
重点是推公式。
我们对于链式纸牌,第一个人只能传给第二个,所以第一个传递的纸牌数为:| T/n-a[1] |。

传出去的纸牌到了第二个,所以考虑前两个,虽然第一个人传给了第二个人,但是两人总数还是a[1]+a[2],为了让两人最后都是T/n,需要传递出去| 2*T/n-a[1]-a[2] |。

如此继续下次可以推得通式为:∑(i从1-n)∣i×T/n−G[i]∣,T为总牌数,G[i]是a[i]的前缀和,ai为编号i的牌堆的牌数,我们利用这个通式就能算出答案(对于链型)。

我们将a[i]都减去T/n变为c[i]结果显然不变,上式变为
∑(i从1-n)∣S[i]∣
其中S[i]为c[i]前缀和。

一个重要的性质:环形纸牌必然可以拆成链式纸牌 归纳可得(由n=2,3等等。
具体证明可看这个博客。

从第k个数拆开,
原数列及其前缀和变为;
C[k+1] S[K+1]-S[K]
C[K+1] S[K+2]-S[K]

C[N] S[N]-S[K]
C[1] S[N]-S[K]+S[1]
C[2] S[N]-S[K]+S[2]

C[K] S[N]-S[K]+S[K]
因为我们减去T/n,所以最后纸牌总和为0,所以S[N]==0

所以答案变为∑(i从1-n)∣S[i]-S[k]∣。

这时,我们可以将s[i]看作数轴上的点,找一个s[k],让s[k]到各个点距离之和最小。
显然,s[k]是s数列的中位数。
证明如下:

当n为s的长度,为偶数时。

中位数有两个记为l和r。
在[l,r]之间任何一个数,确定之后,如果向左移动,那么到左边点的距离减1,
到右边点距离加1,因为是中位数,所以增减相互消除。
如果超出这个界限,那么总会有一边变化量大于另一边。
因为算距离取绝对值,所以答案会增大。所以最小距离和只能在这个区间之中。

为奇数:
中位数只有一个,所以只能取定这个数,否则无论向左取还是向右取,
都会让两边变化量不平衡,导致结果增大。

证明完毕。

最终实现代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
ll n,avg,ans;
int a[maxn],c[maxn];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){cin>>a[i];avg+=a[i];}
    avg/=n;
    for(int i=1;i<=n;i++){c[i]=c[i-1]+a[i]-avg;}
    sort(c+1,c+n+1);
    ll mid=c[(n+1)/2];
    for(int i=1;i<=n;i++)ans+=abs(c[i]-mid);
    cout<<ans<<endl;
    system("pause");
    return 0;
}

最后,如果大家想做有关的题目,
推荐做七夕祭,具体思想也是环形均分纸牌,不过更加复杂。

第二篇文章,希望大家喜欢~~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值