小算法系列-反转棋盘

来源:互联网 发布:adobe 编程字体 编辑:程序博客网 时间:2024/04/30 13:14

最后两步结论的得出很不一般啊~ 一开始确实也被题目迷糊蒙了

 

一个N*M大小的棋盘,每个格子都是0或者1NM都是奇数。你每次可以选择反转一行或者一列,被反转的行或列的所有0变成1,所有1变成0。要求使用最少的反转次数,使得每行每列的1的个数是偶数。

输入格式:从键盘输入,第一行是两个正整数NM,用空格隔开,都不超过20,都是奇数。接下来有N行,每行M个数,都为0或者1,表示棋盘上的数,数字用空格分隔。

输出格式:只有一个数字,表示最少的反转次数。如果不可能通过有限次反转使每行每列的1的个数是偶数,则输出-1

[输入样例A]

3 3

1 1 1

0 1 1

0 0 1

[输出样例A]

2

[输入样例B]

5 3

1 1 1

1 1 1

1 1 1

1 1 1

1 1 1

[输出样例B]

3

#include <iostream>
using namespace std;

int min(int a, int b)
{
 return a < b ? a : b;
}

int main()
{
 int N, M;
 cin >> N >> M; //
读入N行和M
 int Board[20][20]; //
存储棋盘初始情况
 for (int i = 0; i < N; i++)
  for (int j = 0; j < M; j++)
   cin >> Board[i][j];

 int Ro = 0;  // 有奇数个1的行的数目
 for (int i = 0; i < N; i++)
 {
  int temp = 0; //
用于统计每行的1的个数
  for (int j = 0; j < M; j++)
   temp += Board[i][j];
  Ro += temp % 2; //
如果是奇数则加1
 }
 int Co = 0;  //
有奇数个1的列的数目
 for (int j = 0; j < M; j++)
 {
  int temp = 0; //
用于统计每列的1的个数
  for (int i = 0; i < N; i++)
   temp += Board[i][j];
  Co += temp % 2; //
如果是奇数则加1
 }

 if (Ro % 2 == 0)  // 是偶数
  
cout << min(Ro + Co, N - Ro + M - Co) << endl;//解释在下面
 else
  
cout << min(Ro + M - Co, N - Ro + Co) << endl;//解释在下面
 return 0;

 

乍一看被这个题目吓了一下,没摸清方向,只是觉得转一次行的话,列的又变了,感觉似乎有点复杂。静下心来想想似乎也不是那么复杂了。

 

仔细分析一下这个题目,如果行反转奇数次的话,则列的奇偶会变化,但是如果反转偶数次的话,则列的奇偶不会变化。并且行反转奇数次的话,那么列的1的奇偶数目会交互,比如列原来有2个奇数列一个偶数列,则行反转1次后,列变为1个奇数列两个偶数列。

 

因此,如果行的为奇数个1的行数为奇数个的话,则列肯定也是奇数个,不然就没办法达到最终结果。(此过程验证了行和列中1为奇数的数目必须同时为奇数或者同时为偶数

 

OK,现在假设N*M的棋盘,行为奇数个1的行数目以及偶数个1的行数目分别为x,y,列的为p,q,显然x + y = N, p + q = M,且如果x为奇数的话,p也为奇数。

1、现在假设我们优先反转行为偶数,则显然我们需要反转x次,如果x为偶数的话,p肯定也为偶数,且反转完之后pq没有变化,接着需要反转p次列,既总的反转次数为x+p次。而如果x为奇数的话,p肯定也为奇数,且反转完之后,pq交互了,既反转完后,列的为奇数个1的列数目为偶数,因为M为奇数,p为奇数,则q必然为偶数,则列需要反转q次,总的次数为M-p+x (x + q)

。。。。其他过程同理

 

先反转奇数

x为奇数时,行优先总反转次数为:M-p+x  列优先总反转次数为:N-x+p

x为偶数是,行优先和列优先一样,总反转次数为:x + p

先反转偶数

x为奇数时,行优先总反转次数为N-x+p,列优先总反转次数为:M - p + x

x为偶数是,行优先和列优先一样,总反转次数为N-x + M-p

 

得出:

1:行和列里1个数都为偶数时,最少次数为x + p N-x+p 里面较小的那个。

 

2:行和列里1个数都为奇数时,最少次数为N-x+p M - p + x 里面较小的那一个。

 

 

原创粉丝点击