均分纸牌 及 环形均分纸牌
均分纸牌
题目描述
有N堆纸牌,编号分别为 1,2,…,N每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N−1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上,每次只能移一张牌
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多
思路
(前言纸牌总数必为n的倍数保证一定有解
不难想到最终每堆牌上的数即为平均数 \(\frac{\sum_{i=1}^{n}a[i]}{n}\)
具体操作及每一堆减去平均数后,从第一堆开始枚举,如果数大于零说明多于平均数要给其他堆,反之需要从别个堆哪里拿过来
\(ans=\sum_{i=1}^{n} sum[i]-i*aver\)
sum[i]为前i堆的前缀和,aver为平均数
类似题目NOIP2002 提高组均分纸牌(一次移多次题目
题目描述
简明题意:
行列分别为环形的均分纸牌
思路
首先要考虑满足是否有解,同上,只有各行摊点总数为行数的倍数,一定有解,同理,列数亦然
再考虑求解
假设\(S_{i}\) 为\(S_{i}\)给\(S_{i+1}\)的牌数,如果\(S_{i}\)为负数,则为\(S_{i+1}\)给\(S_{i}\)的牌数,再设\(S_{n}\)为给\(S_{1}\)的牌数,这样就构成了环
(这里有点像向量,数学好的同学可以从向量的角度理解
则答案 \(ans=\sum_{i=1}^{n}|S_{1}|\)
接着因为均分后的纸牌都为aver,则可以推出下列式子
\(a[1]+S_{n}-S_{1}=aver\)
简单解释下这个式子,第一堆变为aver,加上\(S_{n}\)给它的纸牌,减去\(S_{1}\)给出去的纸牌
同理
\(a[2]+S_{1}-S_{2}=aver\)
\(a[3]+S_{2}-S_{3}=aver\)
........
\(a[n]+S_{n-1}-S_{n}=aver\)
因为答案的和式与\(S_{i}\)有关
尝试用其他形式将\(S_{i}\)表达出来
根据上述已有关系
可以推出
\(S_{2}=a[2]+S_{1}-aver\)
\(S_{3}=a[3]+S_{2}-aver=S_{1}-(2*aver-(a[2]+a[3]))\)
\(S_{4}=a[4]+S_{3}-aver=S_{1}-(3*aver-(a[2]+a[3]+a[4]))\)
........
\(S_{n}=a[n]+S_{n-1}-aver=S_{1}-\sum_{i=2}^{n}(aver-a[i])\)
令 \(M_{i}=\sum^{i}_{j=2}(aver-a[j])(2<=i<=n)\)
\(S_{i}=S_{1}-M_{i}\)
\(ans=\sum_{i=1}^{n}|S_{1}|=\sum_{i=2}^{n}(|S_{1}-M_{i}|)+S_{1}\)
设m[1]=0;
则\(ans=\sum_{i=1}^{n}|S_{1}|=\sum_{i=1}^{n}(|S_{1}-M_{i}|)\)
(注意这里\(M_{i}\)是可以求出来的,aver和a[i]都是已知的
接下来关键就是怎么求\(S_{1}\)
其实通过观察这和式
可以举个例子 |a-3|+|a+3|为最小值时 a的值为?
运用初中数轴的知识 易得-3<=a<=3
推广一下,设序列\(b_{i}\)
\(|a-b_{1}|+|a-b_{2}|+.....+|a-b_{k}|+.....|a-b_{n}|\)
其实此时是这个式子最小的数为b_{i}的中位数
- 来看一下中位数的定义就知道为什么
中位数(Median)又称中值,统计学中的专有名词,是按顺序排列的一组数据中居于中间位置的数,代表一个样本、种群或概率分布中的一个数值,其可将数值集合划分为相等的上下两部分。对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数 ---来自百度百科(侵删
所以再看这个\(\sum_{i=1}^{n}|S_{1}-M_{i}|\)
将\(M_{i}\)求出来,排序令\(S_{i}=M序列的中位数\)
设M序列长度为n
当为n偶数,则\(S_{i}∈[S_{\frac{n}{2}}, S_{\frac{n}{2}+1}]\)
当为n奇数,则\(S_{i}=S_{\frac{n}{2}+1}\)
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,t;
const int maxx=1e5+10;
int r[maxx],c[maxx];
long long cz(int *a,int len){
int aver=t/len;
long long sum=0;long long m[maxx];m[1]=0;
for(int i=2;i<=len;++i){
sum+=a[i];
m[i]=(i-1)*aver-sum;
}sort(m+1,m+1+len);
int mid=m[len/2+1];long long ans=0;
for(int i=1;i<=len;++i)
ans+=abs(mid-m[i]);
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&t);
for(int i=1;i<=t;++i){
int x,y;
scanf("%d%d",&x,&y);
r[x]++,c[y]++;
}
if(t%n && t%m){
printf("impossible");
return 0;
}if(t%n==0 && t%m==0 ){
printf("both %lld",(long long)(cz(r,n)+cz(c,m)));
return 0;
}
else if(t%n==0){
printf("row %lld",cz(r,n));
return 0;
}else printf("column %lld",cz(c,m));
}
ZFY AK IOI