Codeforces 1107D - Compression(思维 + 二维前缀和)

在这里插入图片描述在这里插入图片描述

题目大意:

给出一个n x n 的矩阵A,只有01两种元素组成,确保给的一定是4的倍数,接下来要对该矩阵进行压缩,压缩后的矩阵为B,边长为x,要求A i j 等于比B i / x j / x (i / x 向上取整)。如n = 8,压缩后的x为2,所以原来a 1 1 就等于 B 1 1 B1 2 B 2 1 B 2 2。询问压缩后的矩阵最大边长是多少,先输入一个n,然后每行输入一个n / 4位的十六进制数,需要先将十六进制数转为二进制的01存入矩阵。

解题思路:

要使得1个元素的值等于x * x 个元素的值,就要使这些值都相等,而该矩阵的值只有0和1,就可以把问题转化为:将一个大矩阵转化为n等分的全0或全1的矩阵,矩阵最大边长是多少。,也就是说,我们需要把n x n 的矩阵转化为(n / x)2个矩阵,每个边长是x,这些矩阵元素要么是全0要么是全1,
这样就好办了,维护这个矩阵的二维前缀和,从大到小枚举可能的答案x,然后分块求前缀和,当一块的前缀和 = 0或 = x * x,说明全0或全1,即可num++,最后统计num是不是等于(n / x)2,如果等于说明所有部分都符合要求,退出循环,输出答案即可。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <cstring>
using namespace std;
const int N = 5500;
int mp[N][N], sum[N][N];
int main()
{
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++)//将十六进制转为二进制存入矩阵
	{
		string s;
		cin >> s;
		for (int j =0, idx = 0; j < n / 4; j ++, idx ++)
		{
			int t = isdigit(s[idx]) ? s[idx] - '0' : s[idx] - 'A' + 10;
			mp[i][j * 4 + 1] = (t & 8) >> 3;
			mp[i][j * 4 + 2] = (t & 4) >> 2;
			mp[i][j * 4 + 3] = (t & 2) >> 1;
			mp[i][j * 4 + 4] = t & 1;
		}
	}
	memset(sum, 0, sizeof sum);
	for (int i = 1;  i<= n; i ++)//二维前缀和
	  for (int j = 1; j <= n; j ++)
	    sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mp[i][j];
	int ans = 1;
	bool flag = false;
	for (int i = n; i >= 1; i--)
	{
		if(n % i)  continue;//枚举的部分必须整除才保证可以均分
		int num1 = 0, num2 = 0;
		for (int j = i; j <= n; j += i)
		    for (int k = i; k <= n; k += i)
		    {
			    if (sum[j][k]-sum[j - i][k] - sum[j][k - i] + sum[j - i][k - i] == 0)  num1++;//判断全0或全1
			    else if (sum[j][k]-sum[j - i][k] - sum[j][k - i]+sum[j - i][k - i] == i * i)  num2++;

		    }
		int x = n / i;//一共有x平方个部分
		if (num1 + num2 == x * x)
		{
			flag = true;
			ans = i;
		}
		if (flag)
		  break;
	}
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/aezakmias/article/details/107637172