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;
}
View Code

 

posted @ 2017-07-19 15:47  Captain_fcj  阅读(253)  评论(0编辑  收藏  举报