Acwing——七夕祭(前缀和+中位数+环断链)

思路比较麻烦,但是代码很简洁!

1.由该题联想到均分纸牌这题,但是该题首尾相接,相当于纸牌形成一个环,而且每次只能移动1。
对均分纸牌做一个改变,即每次只能移动一张纸牌,先设纸牌数组为a数组,由平均数想到将a数组每一位减去平均数得到负债数组b(负债可正可负),每堆纸牌只受相邻左右两堆影响,因此从左开始,第i堆的负债(即b[i]),转让给第i+1堆(b[i+1]+=b[i]),第i堆归零,第i+1堆加上第i堆的负债(可正可负),与此同时操作数要加上负债的绝对值,直到最后。可以发现每次加上的操作数是b数组的前缀和的绝对值,设f数组是b数组的前缀和数组,求**∑|f[i]|**即可。

2.但是在这个问题中,纸牌可以首尾相接,即形成一个环。对于环,有一个断环形式,就是考虑从第k堆开始断,形成一条链,同样是求前缀和,利用原来的f数组,此时第k位是f[k]-f[k]=0,第k+1位是f[k+1]-f[k]……第n位是f[n]-f[k],第1位是f[n]-f[k]+f[1]……第k-1位f[n]-f[k]+f[k+1]根据上面的推论可知:f[n]=0(因为f数组是负债数组b的前缀和,因为肯定存在解,所以当累加到最后一项时负债肯定为0,可以试着把f数组列出),于是可以化简:f[n]-f[k]+f[1]=f[1]-f[k],类似的最后求的就是∑|f[i]-f[k]|,为了使操作数最少,就要找出使∑|f[i]-f[k]|最小的k,由中位数的性质可知,f[k]就是f数组的中位数,于是解决了环形均分纸牌的问题

3.在这道题目中,先进行特判,如果喜爱的摊位数是行或列的整数,则分别求行或列的操作数,如果是行列的公倍数,因为交换两个元素时只影响行或列其一,因此分别求出行和列的操作数即可!

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int row[N] , col[N];//row[i]表示第i行的元素数,col类似
int n , m , k;
int f[N];

long long cal(int a[] , int t)
{
    for(int i = 1 ; i <= t ; i++)//求减掉平均值的前缀和
    {
        a[i] -= k/t;
        f[i] = f[i - 1] + a[i];
    }
    
    sort(f + 1 , f + 1 + t);
    long long res = 0;
    for(int i = 1 ; i <= t ; i++)//利用中位数思想,求出最优解
        res += abs(f[i] - f[(t+1) >> 1]);
        
    return res;
}

int main()
{
    cin >> n >> m >> k;
    
    for(int i = 0 ; i < k ; i++)
    {
        int x , y;
        cin >> x >> y;
        row[x]++ , col[y]++;//对应行的元素的个数+1
    }
    
    if(k % n == 0 && k % m == 0) cout << "both " << cal(row , n) + cal(col , m) << endl;
    else if(k % n == 0) cout << "row " << cal(row , n) << endl;
    else if(k % m == 0) cout << "column " << cal(col , m) << endl;
    else cout << "impossible" << endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值