算法竞赛入门经典训练指南打卡
题目链接:UVA 11464
思路
如果我们要写出一个上述的偶数矩阵,肯定是先确定第一排,然后再根据第一排确定第二排,通过第一排和第二排确定第三排。也就是说我们必须要先确定第一排的数,再推导下面的数。
而每个数都可以是0或者1,所以第一排我们可以使用2进制来确定第一排的数。这样可以使第一排的每种可能都出现。然后我们再通过第一排确定第二排,一直到写出整个偶数矩阵为止。此时判断该矩阵与我们输入的矩阵有多少个地方是变化了的即可
具体的细节可以看代码注释
代码如下:
#include <iostream>
#include <cstring>
#define ton(i , n) for(int i = 0 ; i < n ; ++ i)
using namespace std ;
int a[15][15] , b[15][15] ;
int n ;
int change(int first){
memset(b , 0 , sizeof(b)) ; //将b数组置零
ton(i , n){
if(first & (1 << i)) //给第一排赋值 当前位置为1 ,则是可行的。因为此时无论输入的数组此时是0还是1,都可以是1
b[0][i] = 1 ;
else{
if(a[0][i] == 1)
return 300 ; //当前位置为0,则输入的数组该位置必须为0,否则该方案行不通
}
}
for(int i = 1 ; i < n ; ++ i)
ton(j , n){
int sum = 0 ;
if(i > 1)
sum += b[i - 2][j] ; //i > i 的时候是第三批到最后一排,此时需要计算该位置的上上层对应的数
if(j > 0)
sum += b[i - 1][j - 1] ; //j > 0 此时左边不是边界,所以需要加上上一层左边的数字
if(j < n - 1)
sum += b[i - 1][j + 1] ; //j < n - 1的时候右边不是边界,需要加上上一层右边的数字
b[i][j] = sum % 2 ; //给当前位置赋值
if(a[i][j] == 1 && b[i][j] == 0) //如果当前位置为0,输入数组对应位置为1,则该方案不可行
return 300 ;
}
int ans = 0 ;
ton(i , n)
ton(j , n)
if(a[i][j] != b[i][j])
ans ++ ; //计算a数组和b数组中不同的数的个数,即我们改变的数字
return ans ;
}
int main(){
int t , k = 1 ;
cin >> t ;
while (t --){
cin >> n ;
ton(i , n)
ton(j , n)
cin >> a[i][j] ; //输入矩阵到a
int ans = 300 ; //最大的也是15 * 15的矩阵,不可能改变300次
ton(i , (1 << n))
ans = min(ans , change(i)) ; //枚举第一层
if(ans == 300)
ans = -1 ; //如果ans没有变化则说明不存在可以变为偶数矩阵的情况
cout << "Case " << k ++ << ": " << ans << endl ;
}
return 0 ;
}