codeforces 710C Magic Odd Square
题目链接:http://codeforces.com/problemset/problem/710/C
Find an n × n matrix with different numbers from 1 to n2, so the sum in each row, column and both main diagonals are odd.
The only line contains odd integer n (1 ≤ n ≤ 49).
Print n lines with n integers. All the integers should be different and from 1 to n2. The sum in each row, column and both main diagonals should be odd.
1
1
3
2 1 4
3 5 7
6 9 8
题意:构造一个元素为1~n^2的矩阵,使得各行各列的和都是奇数。
这道题应该有各种五花八门的构造方法,有一种是转换成构造各行各列的和都是n*(n*n+1)/2的幻方:http://www.cnblogs.com/vincentX/p/5798195.html
这里写一下比赛时我的构造思路。
首先行列和的奇偶性只和奇数的个数有关,而和具体的元素值无关,所以构造时只要考虑(n*n+1)/2个奇数位置怎么安排就行了,具体放哪些值都没有关系。很明显要使各行各列的元素和都是奇数,那么每一行和每一列都要保证有奇数个奇数。
从样例不难推出当n为奇数时的构造方法,比较简单,下面直接给出n为11时奇数元素的位置,1表示该位置为奇数,0表示该位置为偶数,直接看就行了:
0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 1 0 0 0
0 0 1 1 1 1 1 1 1 0 0
0 1 1 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 1 1 0 0
0 0 0 1 1 1 1 1 0 0 0
0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0
n为偶数的时候,可以这样想,首先要保证每一行每一列的奇数个数都是奇数,那么一开始先沿着对角线放一排奇数肯定是没有问题的,以n等于6为例:
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
然后还剩下6*6/2-6=12个奇数,怎么安排呢?
假设我们随便找一个还未放置奇数的位置,放一个奇数,这时候该位置对应的行和列奇偶性都会被改变,下图中涂黑的表示不合法的行和列:
1 1 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
那么为了让不合法的行和列元素和变回奇数,那么就需要再放一个奇数,比如我们在第一行对称的地方再放一个奇数,就变成这样:
1 1 0 0 1 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
接下来只要在不合法的那两列都各放上一个奇数,矩阵就又变成合法的了,为了构造方便,我们放的时候保持对称,就变成这样:
1 1 0 0 1 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 1 0 0 1 1
经过以上几步之后,我们成功地放进了四个奇数并使矩阵保持合法。
如果按照这种方法可以把所有奇数都放完,那么这种构造方法就是可行的。
首先可以发现,在对角线上放了一排奇数之后,对于任意一个还未放置的位置,其另外三个对称点必然也是未放置的:
所以放置的时候不会产生冲突,于是我们只要考虑一下放完对角线元素之后按照上述步骤这样四个四个地放能不能刚好把剩下还未放置的奇数放完就行了。
放完对角线元素之后还剩n*n/2-n个奇数需要放置,也就是说我们只要证明n*n/2-n可以被4整除就行了。
证明:
要证n*n/2-n可以被4整除,即证n(n-2)/2可以被4整除。
又n(n-2)/2/4=(n/2)((n-2)/2)/2
由于n是偶数,可得n|2且(n-2)|2,即n/2和(n-2)/2都是整数且是相邻的整数。
设n/2=p, p为整数。
则(n-2)/2=p-1
(n/2)((n-2)/2)/2=(p-1)*p/2
相邻的两个整数必有一个是偶数,易得(p-1)*p/2也是整数,所以n(n-2)/2可以被4整除。
做完这个证明,也就相当于证明了这个构造方法的正确性,接下来就直接上代码就行了:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX=50; 4 int A[MAX][MAX]; 5 6 int main() 7 { 8 int n; 9 scanf("%d",&n); 10 if(n%2) 11 { 12 for(int i=1; i<=n/2+1; i++) 13 for(int j=n/2+2-i; j<=n/2+i; j++) 14 A[i][j]=A[n-i+1][j]=1; 15 } 16 else 17 { 18 for(int i=1; i<=n; i++) 19 A[i][i]=1; 20 21 int cnt=n*n/2-n; 22 for(int i=1; i<=n/2; i++) 23 for(int j=1; j<=n/2; j++) 24 { 25 if(!cnt) 26 goto flag; 27 if(!A[i][j]) 28 { 29 A[i][j]=A[n-i+1][j]=A[i][n-j+1]=A[n-i+1][n-j+1]=1; 30 cnt-=4; 31 } 32 } 33 } 34 35 flag: 36 int tmp1=1,tmp2=2; 37 for(int i=1; i<=n; i++) 38 for(int j=1; j<=n; j++) 39 if(A[i][j]) 40 { 41 A[i][j]=tmp1; 42 tmp1+=2; 43 } 44 else 45 { 46 A[i][j]=tmp2; 47 tmp2+=2; 48 } 49 50 for(int i=1; i<=n; i++) 51 { 52 for(int j=1; j<=n; j++) 53 printf("%d ",A[i][j]); 54 printf("\n"); 55 } 56 57 return 0; 58 }