神奇矩阵
如图所示,所谓神奇矩阵就是将
1
1
1 ~
9
9
9 这
9
9
9 个数排成
3
3
3 行
3
3
3 列,使其行、列、对角线上
3
3
3 个数之和相同。试输出所有神奇矩阵的排序方式。

输入格式
无输入。
输出格式
输出所有神奇矩阵的排序方式,每种之间空一行。
解题思路
这道题最容易想到的算法是枚举法,但其时间复杂度高达 O ( n 3 ) O(n^3) O(n3)。伪代码如下所示:
for(int i=159; i<=951; i++)//枚举第1行数
for(int j=159; j<=951; j++)//枚举第2行数
for(int j=159; k<=951; k++)//枚举第3行数
{
将i、j、k这3个三位数拆成9个一位数
判断3行上的3个数之和是不是15
判断3列上的3个数之和是不是15
判断两2对角线上的3个数之和是不是15
判断9个数是否重复
}
进一步的优化是将每一行看作一个三位数,首先找出所有三位上的数之和为 15 15 15 ,且把三位上的各不相同的三位数一次存入数组 a r y [ ] ary[] ary[]。
再通过枚举的方法从 a r y [ ] ary[] ary[] 中任取 3 3 3 个数,为方便起见,将放在第 1 1 1 行的数编号为 1 1 1 ,将放在第 2 2 2 行的数编号为 2 2 2 ,将放在第 3 3 3 行的数编号为 3 3 3 ,显然这 3 3 3 行数共有 6 6 6 种排列方式,分别为 ( 1 , 2 , 3 ) (1,2,3) (1,2,3)、 ( 1 , 3 , 2 ) (1,3,2) (1,3,2)、 ( 2 , 1 , 3 ) (2,1,3) (2,1,3)、 ( 2 , 3 , 1 ) (2,3,1) (2,3,1)、 ( 3 , 1 , 2 ) (3,1,2) (3,1,2) 及 ( 3 , 2 , 1 ) (3,2,1) (3,2,1)。
因为从 3 3 3 行数中取 1 1 1 行数放在第 1 1 1 行有 3 3 3 种方法,再取 1 1 1 行数放在第 2 2 2 行有 2 2 2 种方法,最后 1 1 1 行数放在第 3 3 3 行只有 1 1 1 种方法,由乘法原理得 3 × 2 × 1 = 6 3×2×1=6 3×2×1=6 种方法。更进一步,记 A n m A^m_n Anm 表示从 n n n 个不同的元素中任取 m ( m ≤ n ) m(m \le n) m(m≤n) 个元素,计算公式为 A n m = n ( n − 1 ) ( n − 2 ) A^m_n=n(n-1)(n-2) Anm=n(n−1)(n−2)··· ( n − m + 1 ) (n-m+1) (n−m+1)。参考程序如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a,b,c,n=0,ary[100];
for(int i=159; i<=951; i++)
{
a=i/100;
b=i/10%10;
c=i%10;
if(a+b+c==15&&a!=c&&a!=b&&b!=c&&a*b*c!=0)
ary[n++]=i;
}
int a1,b1,c1,a2,b2,c2,a3,b3,c3;
for(int i=0; i<n; i++)
for(int j=i+1; j<n; j++)
for(int k=j+1; k<n; k++)
{
a1=ary[i]/100;b1=ary[i]/10%10;c1=ary[i]%10;
a2=ary[j]/100;b2=ary[j]/10%10;c2=ary[j]%10;
a3=ary[k]/100;b3=ary[k]/10%10;c3=ary[k]%10;
if(a1+a2+a3==15&&b1+b2+b3==15&&c1+c2+c3==15&&a1*b1*c1*a2*b2*c2*a3*b3*c3==362880)
{
if(a1+b2+c3==15&&c1+b2+a3==15)
printf("%d\n%d\n%d\n\n",ary[i],ary[j],ary[k]);
if(a1+b3+c2==15&&c1+b3+a1==15)
printf("%d\n%d\n%d\n\n",ary[i],ary[k],ary[j]);
if(a2+b1+c3==15&&c2+b1+a3==15)
printf("%d\n%d\n%d\n\n",ary[j],ary[i],ary[k]);
if(a2+b3+c1==15&&c1+b3+a2==15)
printf("%d\n%d\n%d\n\n",ary[j],ary[k],ary[i]);
if(a3+b2+c1==15&&c3+b2+a1==15)
printf("%d\n%d\n%d\n\n",ary[k],ary[j],ary[i]);
if(a3+b1+c2==15&&c3+b1+a2==15)
printf("%d\n%d\n%d\n\n",ary[k],ary[i],ary[j]);
}
}
return 0;
}
当然,我们还可以继续深入。仔细推算可以发现,根据其奇偶性质,满足条件的只有下面的这一种情况:

而偶数仅有
2
2
2、
4
4
4、
6
6
6、
8
8
8 这
4
4
4 个数,正好占据了矩阵的
4
4
4 个角。那么到了这里,我们就完全可以根据现有的矩阵填充的数学规律,直接填写出满足条件的一个答案,如下图所示:

上图的填充规律是,在第
1
1
1 行中间填
1
1
1 ,然后
1
1
1 的右上角即
2
2
2 的位置(行越界则移到最后一行),
2
2
2 的右上角即
3
3
3 的位置(列越界则移到第
1
1
1 列)以此类推。一直到碰到已经填好的数为止,此时数向下填。填好后将此序列进行旋转
90
90
90 度和上下翻转即可。
Code
#include<bits/stdc++.h>
using namespace std;
int a[3][3],b[3][3];
int main()
{
int x=0,y=1;
a[0][1]=1;
for(int i=2; i<=9; i++)
{
int tx=(x+2)%3;
int ty=(y+1)%3;
if(a[tx][ty]==0)
{
a[tx][ty]=i;
x=tx;
y=ty;
}
else
{
x=(x+1)%3;
a[x][y]=i;
}
}
for(int i=0; i<=3; i++)
{
printf("%d%d%d\n",a[0][0],a[0][1],a[0][2]);
printf("%d%d%d\n",a[1][0],a[1][1],a[1][2]);
printf("%d%d%d\n\n",a[2][0],a[2][1],a[2][2]);
printf("%d%d%d\n",a[2][0],a[2][1],a[2][2]);
printf("%d%d%d\n",a[1][0],a[1][1],a[1][2]);
printf("%d%d%d\n\n",a[0][0],a[0][1],a[0][2]);
for(int ii=0; ii<3; ii++)
for(int jj=0; jj<3; jj++)
b[jj][2-ii]=a[ii][jj];
for(int ii=0; ii<3; ii++)
for(int jj=0; jj<3; jj++)
a[ii][jj]=b[ii][jj];
}
return 0;
}
感谢大家的阅读,有不足之处请广大网友在下方评论区指出。戳这里看上一篇文章。拜拜!
617

被折叠的 条评论
为什么被折叠?



