问题:
在一个3*3的方格内填入9个数字,数字的范围从1~9,每个数字仅出现一次,使得每行、每列以及两个对角线的数字之和为15。
算法思路:
1、将9个数字分成3组,每组3个数字,使得每组数字之和为15。
2、将第二个数字为5个分组单独选出,作为方格的第二行数组。
3、循环抽取其它的分组,与第二分组进行重复性判断。
4、如果没有重复,记录这3种分组。
5、将这3种分组传人判断函数,核实是否满足条件。
6、如果满足条件,将结果输出。
VBA源程序如下:
运行结果
6 7 2
Sub jiugongsuan_All()
Dim cs(1 To 9) As Integer
Dim jgs1(1 To 3) As Integer
Dim Ot() As Integer
'第一、三行数组
Dim Sec() As Integer
'第二行数组
Dim temp1(1 To 100, 1 To 3) As Integer
Dim temp2(1 To 100, 1 To 3) As Integer
'temp1、temp2分别为临时数组
Dim Totali As Integer
'第二个数为5,三个数之和为15的数组个数
Dim Totaln As Integer
'不包括第二个数为5,三个数之和为15的数组个数
Dim at(1 To 3) As Integer
Dim bt(1 To 3) As Integer
Dim ct(1 To 3) As Integer
'at、bt、ct分别为中间变量数组
Dim scjg(1 To 3, 1 To 3) As Integer
'scjg为结果数组
Dim i As Integer
Totali = 0
Totaln = 0
For i = 1 To 9
'初始数组赋值
cs(i) = i
Next
For m = 1 To 9
If cs(m) <> 0 Then
' 判断该数字是否已被占用
jgs1(1) = cs(m)
cs(m) = 0
' 占用该数字
For n = 1 To 9
If cs(n) <> 0 Then
jgs1(2) = cs(n)
cs(n) = 0
For q = 1 To 9
If cs(q) <> 0 Then
jgs1(3) = cs(q)
cs(q) = 0
If jgs1(1) + jgs1(2) + jgs1(3) = 15 Then
' Debug.Print jgs1(1); jgs1(2); jgs1(3)
If jgs1(2) = 5 Then
'第二行数据
Totali = Totali + 1
temp1(Totali, 1) = jgs1(1)
temp1(Totali, 2) = jgs1(2)
temp1(Totali, 3) = jgs1(3)
Else
' 第一行,第三行数据
Totaln = Totaln + 1
temp2(Totaln, 1) = jgs1(1)
temp2(Totaln, 2) = jgs1(2)
temp2(Totaln, 3) = jgs1(3)
End If
End If
cs(q) = q
End If
Next
cs(n) = n
End If
Next
cs(m) = m
End If
Next
ReDim Sec(1 To Totali, 1 To 3)
ReDim Ot(1 To Totaln, 1 To 3)
For m1 = 1 To Totali
Sec(m1, 1) = temp1(m1, 1)
Sec(m1, 2) = temp1(m1, 2)
Sec(m1, 3) = temp1(m1, 3)
'第二行数组赋值
Next
For n1 = 1 To Totaln
Ot(n1, 1) = temp2(n1, 1)
Ot(n1, 2) = temp2(n1, 2)
Ot(n1, 3) = temp2(n1, 3)
Next
'第一、三行数组赋值
For m1 = 1 To Totali
bt(1) = Sec(m1, 1)
bt(2) = Sec(m1, 2)
bt(3) = Sec(m1, 3)
For n1 = 1 To Totaln
at(1) = Ot(n1, 1)
at(2) = Ot(n1, 2)
at(3) = Ot(n1, 3)
If cfpd(at, bt) Then
Else
For k1 = 1 To Totaln
ct(1) = Ot(k1, 1)
ct(2) = Ot(k1, 2)
ct(3) = Ot(k1, 3)
If Not cfpd(at, ct) And Not cfpd(bt, ct) Then
' 第一、三行,第二、三行重复情况判断
scjg(1, 1) = at(1)
scjg(1, 2) = at(2)
scjg(1, 3) = at(3)
scjg(2, 1) = bt(1)
scjg(2, 2) = bt(2)
scjg(2, 3) = bt(3)
scjg(3, 1) = ct(1)
scjg(3, 2) = ct(2)
scjg(3, 3) = ct(3)
' 调用函数
finalpd (scjg)
Else
End If
Next
End If
Next
Next
End Sub
Function cfpd(a() As Integer, b() As Integer) As Boolean
'2个数组重复情况判断
If b(1) <> a(1) And b(1) <> a(2) And b(1) <> a(3) Then
cfpd = False
Else
cfpd = True
Exit Function
End If
If b(2) <> a(1) And b(2) <> a(2) And b(2) <> a(3) Then
cfpd = False
Else
cfpd = True
Exit Function
End If
If b(3) <> a(1) And b(3) <> a(2) And b(3) <> a(3) Then
cfpd = False
Else
cfpd = True
Exit Function
End If
End Function
Sub finalpd(sz As Variant)
'列求和、对角线求和判断
Dim i%, j%
Dim myjgs(1 To 3, 1 To 3)
Dim Hpd As Boolean
Dim Lpd As Boolean
Dim DJXpd As Boolean
For i = 1 To 3
For j = 1 To 3
myjgs(i, j) = sz(i, j)
Next
Next
'列判断
For i = 1 To 3
If myjgs(1, i) + myjgs(2, i) + myjgs(3, i) = 15 Then
Lpd = True
Else
Lpd = False
Exit For
End If
Next
'对角线判断
If myjgs(1, 1) + myjgs(2, 2) + myjgs(3, 3) = 15 And myjgs(1, 3) + myjgs(2, 2) + myjgs(3, 1) = 15 Then
DJXpd = True
Else
DJXpd = False
End If
If Lpd And DJXpd Then
'结果输出
Debug.Print myjgs(1, 1); myjgs(1, 2); myjgs(1, 3)
Debug.Print myjgs(2, 1); myjgs(2, 2); myjgs(2, 3)
Debug.Print myjgs(3, 1); myjgs(3, 2); myjgs(3, 3)
Debug.Print "-------------------------"
End If
End Sub
运行结果:
6 7 2
1 5 9
8 3 4
-------------------------
8 3 4
1 5 9
6 7 2
-------------------------
4 9 2
3 5 7
8 1 6
-------------------------
8 1 6
3 5 7
4 9 2
-------------------------
2 9 4
7 5 3
6 1 8
-------------------------
6 1 8
7 5 3
2 9 4
-------------------------
2 7 6
9 5 1
4 3 8
-------------------------
4 3 8
9 5 1
2 7 6
1、选择数据菜单-à“规划求解”
2、设置可变单元格及约束条件
约束条件
Ø 行之和为15
Ø 列之和为15
Ø 所有数字均为整数
Ø 所有数字>=1
Ø 所有数字<=9
Ø 所有数字连乘的积为9的阶乘,这种方式可以限制每个数字出现1次,且仅出现1次。
图一
图二
这种方法只能求出一个解。