http://codeforces.com/problemset/problem/426/D
Sereja has an n × m rectangular table a, each cell of the table contains a zero or a number one. Sereja wants his table to meet the following requirement: each connected component of the same values forms a rectangle with sides parallel to the sides of the table. Rectangles should be filled with cells, that is, if a component form a rectangle of size h × w, then the component must contain exactly hw cells.
A connected component of the same values is a set of cells of the table that meet the following conditions:
every two cells of the set have the same value;
the cells of the set form a connected region on the table (two cells are connected if they are adjacent in some row or some column of the table);
it is impossible to add any cell to the set unless we violate the two previous conditions.
Can Sereja change the values of at most k cells of the table so that the table met the described requirement? What minimum number of table cells should he change in this case?
Input
The first line contains integers n, m and k (1 ≤ n, m ≤ 100; 1 ≤ k ≤ 10). Next n lines describe the table a: the i-th of them contains m integers ai1, ai2, …, aim (0 ≤ ai, j ≤ 1) — the values in the cells of the i-th row.
Output
Print -1, if it is impossible to meet the requirement. Otherwise, print the minimum number of cells which should be changed.
题意:给定n*m的0/1矩阵,最多改变k个位置的数,问能不能使矩阵的每一个连通块都是矩形,求最少改变的位置数。
思路:首先我们要明白一件事情,确定了矩阵的一行,那么其它行也就确定了,要么与确定的那行完全相同,要么完全相反。
可以假证一下,假设第一行是001011101,第一行分为若干个连通块,每个块的下方必须与块完全相同或者完全相反,否则这个块与其下方构成的连通块就一定不是矩形了。然后“缩点”,把每个连通块缩为一点,然后这一行就变成了010101这样交替出现01的一行了,考虑下一行,要么与它完全相同,要么完全相反,否则连通块就一定不能构成矩形了。再往下也是一样的道理,这样我们就证明了这个结论。
然后回到问题中来,如果n或者m<=10,那么直接暴力枚举第一行的状态,顺推剩下的。复杂度O(nm2^10)
如果n和m都>10,那么根据k<=10&&鸽笼原理,至少有一行是没有变化的,枚举没有变化的行就行了。复杂度O(n^2*m)
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int n,m,k;
int a[maxn][maxn];
void rotate()
{
int b[maxn][maxn];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)b[j][i]=a[i][j];
swap(n,m);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=b[i][j];
}
void solve1()
{
int ans=(1<<30);
for(int s=0;s<(1<<m);s++)
{
int b[15];
for(int i=1;i<=m;i++)b[i]=(bool)(s&(1<<(i-1)));
int ret=0;
for(int i=1;i<=n;i++)
{
int num1=0,num2=0;
for(int j=1;j<=m;j++)
if(a[i][j]!=b[j])num1++;
else num2++;
ret+=min(num1,num2);
}
ans=min(ans,ret);
}
if(ans<=k)cout<<ans<<endl;
else cout<<-1<<endl;
}
void solve2()
{
int ans=(1<<30);
for(int choose=1;choose<=n;choose++)
{
int ret=0;
for(int i=1;i<=n;i++)if(i!=choose)
{
int num1=0,num2=0;
for(int j=1;j<=m;j++)
if(a[i][j]!=a[choose][j])num1++;
else num2++;
ret+=min(num1,num2);
}
ans=min(ans,ret);
}
if(ans<=k)cout<<ans<<endl;
else cout<<-1<<endl;
}
int main()
{
//freopen("input.in","r",stdin);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];
if(m<=10)solve1();
else if(n<=10)rotate(),solve1();
else solve2();
return 0;
}