七夕祭(前缀和+中位数)bzoj3032

题意 :有n行m列的矩阵,有t个特殊点,只能通过上下或者左右交换,求使这个矩阵变成每行或者每列或者每行每列的特殊点都一样多,至少需要交换多少次。

分析

  • 将每行每列的特殊点个数相加,交换行只改变列的特殊点的个数,交换列也只能改变每行的特殊点的个数。

  • 这可以将两个数列提出来用两个数组储存行(H[n])列(L[m])的特殊点数,再进行一些操作(+1 -1),使得每个位置上的数字都一样.

  • 这个问题就演变成了经典问题“均分纸牌”(两个均分纸牌n和m),只不过比“均分纸牌”问题多了一个条件:初始位置和结尾位置是可以相连的,那么我们可以按照“均分纸牌”问题来分析这个题目。

  • 均分纸牌 :有n个人排成一列,他们手中分别有一些牌,在每一步操作中,可以让一个人的牌交到他旁边人的手里,求最少需要多少步才能使每个人手上的牌相等。

  • 我们先来分析“均分纸牌”问题怎么解,假设这个问题有解(纸牌总数 % 总人数 == 0)。因为每个人都是固定的位置,如果第一个人的牌小于平均纸牌数,那么第一个人的牌只能从第二个人手中获得,如果第一个人的牌大于平均数,那么第一个人的牌只能交给第二个人。当第一个人达到平均数的时候,第二个人将不能再交给第一个人或者向第一个人索要,所以第二个人又会重复第一个人的操作,直至到最后一个人时,他后面没有人了,所以这个时候所有人都已经达到了平均数,问题结束。

  • 设A[1]~A[n]是第1个人到第n个人手中所持有的纸牌数,S[1] ~ S[n]是纸牌的前缀和,设纸牌平均数是C,C - A[1] 就是代表第一个人要得到这么多纸牌能到达平均数(可正可负),那么第二个人就需要C - [ A[2] - ( C - A[1] ) ] 这么多纸牌才能达到平均数,化简得2C - (A[1] + A[2]),一直到A[n]都是这样,所以得到以下公式。

     n
     ∑	|iC-S[i]|
     i=1
    
  • 如果将每个人的纸牌数都减去平均值,现在A[i] = A[i] - C ,S[i]是A[i]的前缀和那么公式就会变成

     n
     ∑	|S[i]|
     i=1
    
  • 上面的公式只适用于首尾不能交换的情况,那我们来考虑首尾能交换的情况,有种暴力的方法就是枚举第1~n个人,让他们变成第一个人,再对这些人操作,这样必有一个最优解。

  • 另一种方法:在1~n中取一个k,令第k个人变成第一个人,那么A数列排列成
    A[k+1],A[k+2]…A[n],A[1]…A[k] , S数列排列成
    S[k+1]-S[k] , S[k+2]-S[k] … S[n]-S[k] , S[1]+S[n]-S[k] … S[k]-S[k]
    因为S[n]等于0,所以相当与在S[1]~S[n]的基础上减去S[k]。

     n
     ∑	|S[i]-S[k]|
     i=1
    
  • 也就相当于求k(1~n)取何值时以上的值最小

  • 这个公式可以看作有一个数列1~n,要在其中选择一个元素作为一个固定点,求每个点到固定点的最短总距离,这就是“货仓选址”。

  • 货仓选址 :在一条数轴上有n家商店,他们的地址分别是A[1]~A[n],现在要在数轴上建立一个货仓,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在什么何处,可以使得货仓到每家商店的距离之和最小。

  • 先将A[1]~A[n]排序,距离之和最小的位置就是中位数,假设为k(n为奇数,偶数有两个),如果将货仓建在中位数的左边,假设A[k]左边的离A[k]的距离是x,那么左边的距离会少x*(k-2),右边的距离会增加x*(k-1),所以总距离增加了x。

  • 所以回到本题,就可以看作是一个“货仓选址”问题,只需要将S排序找到中位数k,即可求出本体答案。

总结:本题 = 均分纸牌 + 货仓选址

//因为bzoj3032题目显示不出来,所以只写了个大致 
#include<iostream>
using namespace std;
const int N = 1e5+5;
int main()
{
	
	int n,m,k,x,y;
	int H[N]={0},L[N]={0},S[N]={0};
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++){
		cin>>x>>y;
		H[x]++,L[y]++;
	}
	int avgh=0,avgl=0;
	for(int i=1;i<=n;i++)
		avgh += H[i];
	avgh /= n;
	for(int i=1;i<=m;i++)
		avgl += L[i];
	avgl /= m;
	//减去平均值的H和L再算出S 
	
	if(k % n == 0){
		//排序S找中位数,要用结构体cmp排序 懒得写了
	}
	if(k % m == 0){
		
	}
	
	//判断输出哪一种结果 
	
	return 0;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值