tyvj 1924 七夕祭 【数学推理+数形结合】
题在这:http://www.tyvj.cn/p/1924
分析:
通过简单的分析,我们可以得出:因为不能斜着交换,因此横行和竖排的交换是互不相干的。(横排交换的时候竖排没有变换)故,我们就能把横和纵分开分析,二者等价;
二维转化成一维后,它等同于这个样子:
老师准备了一堆糖果, 恰好n个小朋友可以分到数目一样多的糖果. 老师要n个小朋友去拿糖果, 然后围着圆桌坐好, 第1个小朋友的左边是第n个小朋友, 其他第i个小朋友左边是第i-1个小朋友. 大家坐好后, 老师发现, 有些小朋友抢了很多的糖果, 有的小朋友只得到了一点点糖果, 甚至一颗也没有L, 设第i个小朋友有ai颗糖果. 小朋友们可以选择将一些糖果给他左边的或者右边的小朋友, 通过”糖果传递”最后使得每个小朋友得到的糖果数是一样多的, 假设一颗糖果从一个小朋友传给另一个小朋友的代价是1, 问怎样传递使得所耗的总代价最小
首先,可以很容易地判断出排或列有无解,当总摊位数 T 不能够整除排数时,no row; 当总摊位数 T 不能够整除列数时,
no column;当两个均不能整除时,impossible。
我们设第 i 列原本a[i]个摊位, i 列要给 i-1 列x(i)个摊位, i+1 列要给 i 列x (i+1)个摊位,算出平均下来每列应该最终为 ave 个摊位,可得:
a[i] - x(i) + x(i+1) = ave
就像数列一样,我们把它们列出来,找规律:
a[1] - x(1) + x(2) = ave ====》 x(2) = ave - a[1] + x(1) ;
a[2] - x(2) + x(3) = ave ====》 x(3) = ave - a[2] + x(2) ;
a[3] - x(3) + x(4) = ave ====》 x(4) = ave - a[3] + x(3) ;
…… ……
从第一个起: 我们令 a[i] - x(i) = c[i];
其中:c[i] 表示前 i 个列的原本值与平均值之差的前缀和
变形得:
x(2) = x(1) - c[1];
x(3) = x(1) - c[2];
x(4) = x(1) - c[3];
…… ……
于是为了让x(1)+x(2)+x(3)+……+x(i)最小,相当于在数轴上取一点x(1),使得它到c[1] , c[2] , c[3]……的距离之和最小,这显然是要取c[i]的中位数的(可自己证明)。
下面是参考代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; int pai[200000]={0},lie[200000]={0}; int n,m,T; int sum_pai=0,sum_lie=0; int ave_pai,ave_lie; int ans=0; long long c1[200000]={0}; long long c2[200000]={0}; void solve() { if(!(T%n)) { for (int i=1;i<=n;i++) c1[i]=c1[i-1]+pai[i]; sort(c1+1,c1+n+1); long long mid=c1[(n+1)/2]; for(int i=1;i<=n;i++) ans+=abs(c1[i]-mid); } if(!(T%m)) { for(int i=1;i<=m;i++) c2[i]=c2[i-1]+lie[i]; sort(c2+1,c2+m+1); long long mid=c2[(m+1)/2]; for(int i=1;i<=m;i++) ans+=abs(c2[i]-mid); } } int main() { cin>>n>>m>>T; int t=T; while(t--) { int x,y; cin>>x>>y; pai[x]++; lie[y]++; } if(T%n&&T%m) { cout<<"impossible"; return 0; } ave_pai=T/n; ave_lie=T/m; for(int i=1;i<=n;i++) pai[i]=pai[i]-ave_pai; for(int i=1;i<=m;i++) lie[i]=lie[i]-ave_lie; solve(); if(T%n==0&&T%m==0)printf("both "); else if(T%n==0)printf("row "); else printf("column "); printf("%lld\n",ans); return 0; }