思路比较麻烦,但是代码很简洁!
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;
}