题目描述 Description
给你一个n*n的01矩阵(每个元素非0即1),你的任务是把尽量少的0变成1,使得每个元素的上、下、左、右的元素(如果存在的话)之和均为偶数。如图所示的矩阵至少要把3个0变成1,最终如图所示,才能保证其为偶数矩阵。
输入输出格式 Input/output
输入格式:
输入的第一行为数据组数T(T<30)。每组数据的第一行为正整数n(1 < n < 15);接下来的n行每行包含n个非0即1的整数,相邻整数间用一个空格隔开。
输出格式:
对于每组数据,输出被改变的元素的最小个数。如果无解,应输出-1。
输入的第一行为数据组数T(T<30)。每组数据的第一行为正整数n(1 < n < 15);接下来的n行每行包含n个非0即1的整数,相邻整数间用一个空格隔开。
输出格式:
对于每组数据,输出被改变的元素的最小个数。如果无解,应输出-1。
输入输出样例 Sample input/output
样例测试点#1
输入样例:
0 0 0
1 0 0
0 0 0
输出样例:
3
思路:这道题很经典,经典的枚举也有DP的意味在里面,这里我把书上的思路详细化。
书上谈到:抛弃枚举每个数字的方法,这样很大,大概2255这么多的情况,很大,难以接受,可以考虑枚举第一行,这样有215种可能,是可行的,根据第一行算出第二行,以此类推到第n行,时间复杂度降为O(2n×n2)
点石成金罢了,下面来说说如何具体的做到:
将样例的第一行:
0 0 0
可以通过枚举变成
0 1 0
这样第一行就确定下来了,这时候考虑第二行:
0 1 0
α
考虑一下第一行第一列的0,它的上下左右加和=α+1,那么这时候要保证是偶数,即(α+1)%2==0,所以α=1,这时候看看这个α可不可以由原矩阵A的这个位置的数变化而来(即0→1),合法,此时记上1。
0 1 0
1 α
同样计算第一行第二列,(0+α+0)%2==0,α=0,比较原矩阵,合法,记上0。
0 1 0
1 0 α
同上,合法变换矩阵为:
0 1 0
1 0 1
最终执行完:
0 1 0
1 0 1
0 1 0
大致的思路就是这样,要注意的是一些细节之处,比如位运算简化,这样可以使代码简洁很多且运算方便
代码如下(书上copy下来且加了点注释的):
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 const int MAXN=20; 6 const int INF=100000; 7 int A[MAXN][MAXN],B[MAXN][MAXN]; 8 int n; 9 int min(int a,int b) 10 { 11 return a>b?b:a; 12 } 13 int check(int s) 14 { 15 memset(B,0,sizeof(int)); 16 for(int c=0;c<n;c++)//枚举第一行 17 { 18 if(s&(1<<c)) B[0][c]=1; 19 else if(A[0][c]==1) return INF;//不能把1变成0,直接返回 20 } 21 for(int r=1;r<n;r++)//从第二行开始依次筛查 22 { 23 for(int c=0;c<n;c++) 24 { 25 int sum=0;//表示上左右三个元素的和 26 if(r>1) sum+=B[r-2][c]; 27 if(c>0) sum+=B[r-1][c-1]; 28 if(c<n-1) sum+=B[r-1][c+1]; 29 B[r][c]=sum%2; 30 if(A[r][c]==1&&B[r][c]==0) return INF;//违法,不能把1变为0 31 } 32 } 33 int cnt=0; 34 for(int r=0;r<n;r++) 35 { 36 for(int c=0;c<n;c++) 37 { 38 if(A[r][c]!=B[r][c]) cnt++; 39 } 40 } 41 return cnt; 42 } 43 int main() 44 { 45 int r,c;//行、列 46 int i,j; 47 int T; 48 int ans=INF;//初始化为最大值 49 scanf("%d",&T); 50 while(T) 51 { 52 ans=INF; 53 scanf("%d",&n); 54 for(i=0;i<n;i++) 55 { 56 for(j=0;j<n;j++) 57 { 58 scanf("%d",&A[i][j]); 59 } 60 } 61 for(int s=0;s<(1<<n);s++)//1<<n等于2^n,不用pow,比较方便 62 { 63 ans=min(ans,check(s));//不断更新最小ans 64 } 65 if(ans==INF) ans=-1;//没找到答案 66 printf("%d %d ",T,ans); 67 T--; 68 } 69 return 0; 70 }