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