-
由该题联想到均分纸牌这题,但是该题首尾相接,相当于纸牌形成一个环,而且每次只能移动1。
对均分纸牌做一个改变,即每次只能移动一张纸牌,先设纸牌数组为a数组,由平均数想到将a数组每一位减去平均数得到负债数组b(负债可正可负),每堆纸牌只受相邻左右两堆影响,因此从左开始,第i堆的负债(即b[i]
),转让给第i+1
堆(b[i+1]+=b[i]
),第i
堆归零,第i+1
堆加上第i堆的负债(可正可负),与此同时操作数要加上负债的绝对值,直到最后。可以发现每次加上的操作数是b数组的前缀和的绝对值,设f数组是b数组的前缀和数组,求$∑|f[i]|$即可。 -
但是在这个问题中,纸牌可以首尾相接,即形成一个环。对于环,有一个断环形式,就是考虑从第
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$数组的中位数,于是解决了环形均分纸牌的问题 -
在这道题目中,先进行特判,如果喜爱的摊位数是行或列的整数,则分别求行或列的操作数,如果是行列的公倍数,因为交换两个元素时只影响行或列其一,因此分别求出行和列的操作数即可!
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int row[N] , col[N] , f[N];
int n , m , k;
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;
}
赞👍
赞
hh~
赞