Codeforces 426D Sereja and Table

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值