题意:给你个n*m的矩阵,要求你找到一个k,k > 1,使得矩阵可以分为很多k * k的小正方形,然后进行操作把每个小正方形都变为0或1,问你怎样使操作数最小。
思路:随便暴力不可取,显然你每次遍历查找k * k正方形里1和0的数量会超时。这里新学了一招前缀和,其实和二位树状数组差不多。就是预处理前缀和,以达到花O(1)的时间算出(1,1)~(x,y)的和,这样我们就能直接暴力枚举每一种k的操作数,然后取最小即为答案。
参考:前缀和与差分
代码:
#include<stack> #include<vector> #include<queue> #include<set> #include<cstring> #include<string> #include<sstream> #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #define ll long long #define ull unsigned long long using namespace std; const int maxn = 5000+5; const int seed = 131; const int MOD = 100013; const int INF = 0x3f3f3f3f; int sum[maxn][maxn]; char s[maxn]; int get(int x1,int y1,int x2,int y2){ return sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1]; } int main(){ int n,m,k,end; scanf("%d%d",&n,&m); end = max(n,m); memset(sum,0,sizeof(sum)); for(int i = 1; i <= n; i++){ scanf("%s",s + 1); for(int j = 1; j <= m; j++){ sum[i][j] = s[j] - '0'; } } for(int i = 1;i < maxn;i++){ //前缀和 for(int j = 1;j < maxn;j++){ sum[i][j] += sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1]; } } int ans = INF; for(int k = 2; k <= end; k++){ int cnt = 0,endL,endR; if(n % k == 0) endL = n; else endL = (n / k + 1) * k; if(m % k == 0) endR = m; else endR = (m / k + 1) * k; for(int i = 1; i <= endL; i += k){ for(int j = 1; j <= endR; j += k){ int have = get(i,j,i + k - 1,j + k - 1); cnt += min(have,k * k - have); } } ans = min(ans,cnt); } printf("%d\n",ans); return 0; }