Codeforces 426D Sereja and Table【思维+暴力枚举】好题!

D. Sereja and Table
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

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 hwcells.

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 nm and k (1 ≤ n, m ≤ 100; 1 ≤ k ≤ 10). Next n lines describe the table a: the i-th of them contains mintegers 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.

Examples
input
5 5 2
1 1 1 1 1
1 1 1 1 1
1 1 0 1 1
1 1 1 1 1
1 1 1 1 1
output
1
input
3 4 1
1 0 0 0
0 1 1 1
1 1 1 0
output
-1
input
3 4 1
1 0 0 1
0 1 1 0
1 0 0 1
output
0


题目大意:


给出一个n*m的01矩阵,我们最多可以将矩阵中K个位子上的数翻转(0->1 1->0);

问我们最少操作多少次,能够使得每一个联通块都是矩形。如果不存在方案,输出-1.


思路:


①我们知道,对于一个可行方案来讲,第一行上边的情况,要么和其他各行上边的情况相同,要么和其他各个行上边的情况相反。


②进而观察到数据范围,K不大,那么不妨分情况讨论:

1.如果n>K,那么我们肯定至少有一行不会被改变过,那么我们O(n)枚举这样的一行,从而就能够判断出其他各行应该如何去改变,时间复杂度O(n^2*m);

2.如果n<=K,那么我们肯定需要将矩阵翻转过去,然后对于第一行的数据进行O(2^K)枚举每个位子是否进行改变。从而再通过这一行的结果去判断其他各行应该如何去改变,时间复杂度O(2^k*n*m);


过程维护一下即可。

值得注意的点是,我们当前矩阵进行了上述操作取了最小值之后,我们应该再将矩阵翻转一遍,再去做一遍上述过程取整体最小才行。

简单的说就是形状会影响某种情况操作的结果的大小,所以还需要翻转一次取所有方案中的最小值。



Ac代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int b[150][150];
int a[150][150];
int n,m,T,ans;
void Slove()
{
    if(n>T)
    {
        int output=0x3f3f3f3f;
        for(int z=1; z<=n; z++)
        {
            int sum=0;
            for(int i=1; i<=n; i++)
            {
                int cnt=0;
                for(int j=1; j<=m; j++)
                {
                    if(a[i][j]!=a[z][j])cnt++;
                }
                int cnt2=0;
                for(int j=1; j<=m; j++)
                {
                    if(a[i][j]==a[z][j])cnt2++;
                }
                sum+=min(cnt,cnt2);
            }
            output=min(output,sum);
        }
        ans=min(ans,output);
    }
    else if(m<=T)
    {
        int output=0x3f3f3f3f;
        for(int z=0; z<(1<<T); z++)
        {
            int sum=0;
            for(int i=2; i<=n; i++)
            {
                int cnt=0;
                for(int j=1; j<=m; j++)
                {
                    if((z&(1<<(j-1)))>0)
                    {
                        if(a[i][j]==a[1][j])cnt++;
                    }
                    else if(a[i][j]!=a[1][j])cnt++;
                }
                int cnt2=0;
                for(int j=1; j<=m; j++)
                {
                    if((z&(1<<(j-1)))>0)
                    {
                        if(a[i][j]!=a[1][j])cnt2++;
                    }
                    else if(a[i][j]==a[1][j])cnt2++;
                }
                sum+=min(cnt,cnt2);
            }
            int temp=0;
            for(int i=1; i<=m; i++)
            {
                if((z&(1<<(i-1)))>0)temp++;
            }
            output=min(output,sum+temp);
        }
        ans=min(ans,output);
    }
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&T))
    {
        ans=0x3f3f3f3f;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&a[i][j]);
                b[i][j]=a[i][j];
            }
        }
        Slove();
        int x=1,y=1;
        for(int i=1; i<=m; i++)
        {
            for(int j=n; j>=1; j--)
            {
                a[x][y]=b[j][i];
                y++;
                if(y>n)y=1,x++;
            }
        }
        swap(n,m);
        Slove();
        if(ans>T)printf("-1\n");
        else printf("%d\n",ans);
    }
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值