排序--二维环形均分纸牌--七夕祭

二维环形均分纸牌--七夕祭

问题描述

七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。于是TYVJ今年举办了一次线下七夕祭。Vani同学今年成功邀请到了cl同学陪他来共度七夕,于是他们决定去TYVJ七夕祭游玩。  
TYVJ七夕祭和11区的夏祭的形式很像。矩形的祭典会场由N排M列共计N×M个摊点组成。虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多。
不过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。由于zhq率领的TYVJ开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。现在Vani想知道他的两个要求最多能满足多少个。在此前提下,至少需要交换多少次摊点。

输入格式

 第一行包含三个整数N和M和T。T表示cl对多少个摊点感兴趣。
 接下来T行,每行两个整数x, y,表示cl对处在第x行第y列的摊点感兴趣。

输出格式

 首先输出一个字符串。如果能满足Vani的全部两个要求,输出both;如果通过调
 整只能使得各行中cl感兴趣的摊点数一样多,输出row;如果只能使各列中cl感兴
 趣的摊点数一样多,输出column;如果均不能满足,输出impossible。
 如果输出的字符串不是impossible, 接下来输出最小交换次数,与字符串之间
 用一个空格隔开。
  

样例输入

样例输入1
2 3 4
1 3
2 1
2 2
2 3

样例输入2
3 3 3
1 3
2 2
2 3

样例输出

样例输出1
row 1

样例输出2
both 2

原题: 七夕祭

思路解析

经过分析我们可以发现,交换左右两个相邻的摊点 只会改变某两列 中c1感兴趣的摊点数,不会改变每行 中 c1感兴趣的摊点数。
交换上下两个相邻的摊点同理
所以我们可以把本题分成两个互相独立的部分计算,即类比为行,列方向上的两次“环形均分纸牌”问题。而“环形均分纸牌”问题又可类比为从中位数处断环的“线性均分纸牌”问题

线性均分纸牌解析: 看这里

环形均分纸牌解析: 看这里

即:
1、通过最少次数的左右交换使每列中c1感兴趣的摊点数相同。
2、通过最少次数的左右交换使每行中c1感兴趣的摊点数相同。

以第一种情况为例,我们的目标就是让每列中有T/M个 c1感兴趣的摊点,这样就将此问题化简成基本的环形均分纸牌问题了。
我们照常从中位数处断环,然后将它化简为基本的线性均分纸牌问题,再根据前缀和的思想,求出最小代价。
第二种情况同理。
而第三种情况根据上面的分析,就是第一种情况的最小代价加上第二种情况的最小代价

代码

#include<iostream>
using namespace std;
#include<string>
#include<algorithm>


long X[100000];
long Y[100000];
long num_X[100000] = { 0 };
long num_Y[100000] = { 0 };
long S[100000];
long S2[100000];

void main()
{
	// N,M,T
	long N, M, T;
	cin >> N >> M >> T;
	// x行y列
	long x, y;
	for (long i = 0; i < T; i++)
	{
		cin >> x >> y;
		X[i] = x;
		num_X[x]++;
		Y[i] = y;
		num_Y[y]++;

	}
	string ret = "impossible";
	if (T % M && T % N)
		cout << ret;
	// 只能使各行一样多
	else if (!(T % N) && (T % M))
	{
		ret = "row";
		S[N - 1] = 0;
		long avg = T / N;
		long ans = 0;
		// 从中位数处断开
		for (long i = N - 2; i >= 0; i--)
			S[i] = S[i + 1] - (num_X[X[i]] - avg);	
		sort(S, S + N);
		for (long i = 0; i < N; i++) 
			ans += abs(S[i] - S[(N / 2)]);

		cout << ret << " " << ans;

	}
	// 只能使各列一样多
	else if (!(T % M) && (T % N))
	{
		ret = "column";
		S[M - 1] = 0;
		long avg = T / M;
		long ans = 0;	
		// 从中位数处断开
		for (long i = M - 2; i >= 0; i--)
			S[i] = S[i + 1] - (num_Y[Y[i]] - avg);
		sort(S, S + M);
		for (long i = 0; i < M; i++)
			ans += abs(S[i] - S[(M / 2)]);

		cout << ret << " " << ans;

	}
	// 行列均可满足要求
	else
	{
		ret = "both";
		S[N - 1] = 0;
		long avg1 = T / N;
		long ans = 0;
		// 从中位数处断开
		for (long i = N - 2; i >= 0; i--)
			S[i] = S[i + 1] - (num_X[X[i]] - avg1);
		sort(S, S + N);
		for (long i = 0; i < N; i++)
			ans += abs(S[i] - S[(N / 2)]);

		S2[M - 1] = 0;
		long avg2 = T / M;
		// 从中位数处断开
		for (long i = M - 2; i >= 0; i--)
			S2[i] = S2[i + 1] - (num_Y[Y[i]] - avg2);
		sort(S2, S2 + M);
		for (long i = 0; i < M; i++)
			ans += abs(S2[i] - S2[(M / 2)]);

		cout << ret << " " << ans;

	}


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值