开灯关灯问题的两种解决方式
1.问题介绍
2.编程实现算法及其代码(详细)
3.就该题进行数学建模–体验线性代数的使用
1.问题介绍
有一个按钮组成的矩阵,其中每行有5个按钮,共5行。每个按钮旁边有一盏灯。当按下一个按钮,该按钮及其上下左右四个按钮控制的灯都会发生状态改变,即,如果灯原来是熄灭的,就会被点亮;如果灯原来是点亮的,就会被熄灭。(在矩阵角上的按钮会改变3盏灯的状)请写一个程序确定需要按下那些按钮,恰好使得所有灯都熄灭。
2.编程实现算法及其代码(详细)
1.解题思路
对于第一行每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、列的按钮,可以熄灭前4列的灯。此时如果 第5行灯都是熄灭的 那么就得到了问题的解。有了基本思想以后,要解决两个问题: |
1.对应的按钮
为了叙述方便,为按钮矩阵中的每个位置分别指定一个坐标。用数组元素 puzzle[i][j] 表示位置 (i, j) 上灯的初始状态:0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。用数组元素 press[i] [j] 表示为了让全部的灯都熄灭,是否要按下位置 (i, j) 上的按钮:1 表示要按下;0 表示不用按下。
因为灯的最后状态(按它下一行开关之前)与周围灯是否按按钮有关,如 puzzle[i] [j],决定它最后状态的相关按钮为 press[i] [j-1] (左),press[i][j](它本身),press[i] [j+1](右),press[i-1][j](上),还有它最初的状态 puzzle[i] [j]。考虑到按两次按钮作用会抵消,需要取它们的和与2的余数,所以puzzle[i] [j] 最后状态公式为:
puzzle[i][j]最后状态 = (press[i][j-1]+press[i][j]+press[i][j+1]+press[i-1][j]+ puzzle[i] [j] ) % 2
而press[i+1] [j](对应按钮) 由 puzzle[i] [j] 的最后状态决定,灯亮着(值为1)就要关掉它,press值取1,灯是熄灭的(值为0)不用处理,press值取0,它们是相等的,所以press[i+1] [j] = puzzle[i] [j] 最后状态。
2.验证第5行灯是否都熄灭
易知puzzle[5] [j] 的最终状态由按钮press[5] [j-1] (左),press[5][j](它本身),press[5] [j+1](右),press[4] [j](上)决定
当puzzle[5][j]为1时,即第5行j列灯亮着,则分为两种情况
(1). puzzle[5][j] 初始 =1,(press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j])%2=0 即操作相互抵消
(2). puzzle[5][j]初始 =0,(press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j]%2=1即操作不能抵消
注意这两种情况都是 puzzle[5][j]初始 !=press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j]%2
由此可知, 当我们输入每一行的初始状态puzzle[i][j],并且枚举第一行的press[1][j]时,我们就可以根据解题思路得到一个可行解。
2.代码如下:(附有注释)
#include<stdio.h>
int press[6][8];
int puzzle[6][8];
bool guess(){
int i,j;
for(i=2;i<=5;i++)
{
for(j=1;j<=6;j++)
{
// 根据同列的上一行灯的最后状态,来决定是否按按钮
press[i][j] = ( press[i-1][j]
+ puzzle[i-1][j]
+ press[i-1][j-1]
+ press[i-2][j]
+ press[i-1][j+1] ) %2;
}
}
for(j=1;j<=6;j++)
{
// 逐一判断第五行的灯是否都熄灭
if( puzzle[5][j] != (press[5][j] + press[5][j-1] + press[5][j+1] + press[4][j]) %2 )
return false;
}
return true;
}
void process() {
int c;
for(c=1; c<=6; c++)
press[1][c]=0;
while( !guess() )
{
// 采用二进制进位的算法,从000000 - 111111枚举第一行按钮的方式
press[1][1]++;
c=1;
while(press[1][c]>1)
{
press[1][c]=0;
c++;
press[1][c]++;
}
}
}
int main(){
int t,i,n,j;
// 初始化0行的所有元素
for(i=0;i<8;i++)
press[0][i]=puzzle[0][i]=0;
// 初始化0列,7列的所有元素
for(i=1;i<6;i++)
press[i][0]=puzzle[i][0]=press[i][7]=puzzle[i][7]=0;
for(i=1;i<=5;i++)
for(j=1;j<=6;j++)
scanf("%d",&puzzle[i][j]);
process();
for(i=1;i<=5;i++){
for(j=1;j<=6;j++){
printf("%d ",press[i][j]);
}
printf("\n");
}
return 0;
}
这是我自己的理解,参照原文链接: https://blog.csdn.net/qianli_jiang/article/details/53420362?utm_source=app&app_version=4.5.2
3. 就该题进行数学建模——体验线性代数的应用
以下针对从全部熄灭到全部亮起的情况,0,1分别表示熄灭和亮着。 (在看以下内容前可以学习一下线性空间定义和线性空间中基,维数)
定义状态空间S为按钮矩阵上所有可能的状态组成的集合 ,即
S
=
(
e
1
,
e
2
,
.
.
.
,
e
25
)
∣
e
i
=
0
,
1
,
i
=
1
,
2
,
.
.
.
,
25
S= {(e_1,e_2,...,e_{25}) | e_i=0,1, i=1,2,...,25}
S=(e1,e2,...,e25)∣ei=0,1,i=1,2,...,25
显然灯全亮状态为
s
1
s_1
s1={1,1,1…,1}
∈
\in
∈S,全暗状态为
s
0
s_0
s0={0,0,0,…0}
∈
\in
∈S
定义S上的一个变换t :
s
=
(
e
1
,
e
2
,
.
.
.
,
e
25
)
→
s
′
=
(
h
1
,
h
2
,
.
.
.
,
h
25
)
,
s
,
s
′
∈
S
s=(e_1,e_2,...,e_{25}) \rightarrow s' =(h_1,h_2,...,h_{25}) , s,s'\in S
s=(e1,e2,...,e25)→s′=(h1,h2,...,h25),s,s′∈S 其中
h
i
=
e
i
或
者
1
−
e
i
h_i=e_i 或者1-e_i
hi=ei或者1−ei,特别的定义零变换为恒等变换
O
(
e
1
,
e
2
,
.
.
.
,
e
25
)
=
(
e
1
,
e
2
,
.
.
.
,
e
25
)
O(e_1,e_2,...,e_{25}) =(e_1,e_2,...,e_{25})
O(e1,e2,...,e25)=(e1,e2,...,e25)
设V是S上所有变换所组成的集合, 对任意f,g
∈
\in
∈V,定义V中的加法运算为变换合成运算,记为f
∘
\circ
∘g,并用
∑
\sum
∑表示加法运算
∘
\circ
∘的连加符号,例如:
f
1
∘
f
2
∘
f
3
.
.
.
.
.
∘
f
25
=
∑
i
=
1
25
f
i
f_1\circ f_2\circ f_3.....\circ f_{25}=\sum_{i=1}^{25}f_i
f1∘f2∘f3.....∘f25=∑i=125fi
容易验证该运算满足下列性质:
(1).f ∘ \circ ∘g=g ∘ \circ ∘f ∀ \forall ∀f,g ∈ \in ∈V; (2).(f ∘ \circ ∘g) ∘ \circ ∘t= f ∘ \circ ∘(g ∘ \circ ∘t); ∀ \forall ∀f,g,t ∈ \in ∈V; (3).对 ∀ \forall ∀f ∈ \in ∈V,f ∘ \circ ∘f=O |
性质3告诉我们: 任何一种点击方式(变换)连续或间断操作偶数次,此操作的作用将被抵消,故为了使操作次数尽量少,应该避免重复操作。
为此考虑二元数域
F
2
F_2
F2={0,1}(同一操作次数<=1) ,类似于实数域和有理数域,同样可以在
F
2
F_2
F2进行加减乘除,运算法则如下:
+ | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
- | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
× \times × | 0 | 1 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
÷ \div ÷ | 0 | 1 |
---|---|---|
0 | 无意义 | 0 |
1 | 无意义 | 1 |
那么我们可以定义二元域 F 2 F_2 F2到V的数乘运算:1* f=f ,0*f=O, ∀ \forall ∀f ∈ \in ∈V ,容易验证:
V是 F 2 F_2 F2上的线性空间(8个性质) |
将所有的方格按照从左到右从上到下依次编号为1到25,用
g
i
g_i
gi,i=1,2,3,…25表示鼠标点击第i个方格所产生的变换,记一次可行的游戏策略为x(一个25维向量)=
(
x
1
,
x
2
,
.
.
.
.
x
25
)
T
(x_1,x_2,....x_{25})^T
(x1,x2,....x25)T,其中
x
i
x_i
xi=0,1
则x满足:
∑
i
=
1
25
(
x
i
∗
g
i
)
(
s
0
)
=
s
1
\sum_{i=1}^{25}(x_i*g_i)(s_0)=s_1
∑i=125(xi∗gi)(s0)=s1 关键是求出x.
为此定义变换
f
i
∈
V
f_i\in V
fi∈V,i=1,2,…25为
f
i
(
e
1
,
e
2
,
.
.
.
,
e
i
,
.
.
.
e
25
)
=
(
e
1
,
e
2
,
.
.
.
,
1
−
e
i
,
.
.
.
,
e
25
)
f_i(e_1,e_2,...,e_i,...e_{25}) =(e_1,e_2,...,1-e_i,...,e_{25})
fi(e1,e2,...,ei,...e25)=(e1,e2,...,1−ei,...,e25)(神来之笔啊) 由定义知,
f
i
f_i
fi只会改变第i个方格的状态,而不会影响到别的方格, 容易得到{
f
i
f_i
fi |i=1,2,…25}是线性空间V的一组基,故
g
i
g_i
gi可以唯一的用{
f
i
f_i
fi}表示为
g
i
=
f
i
∘
f
i
−
5
∘
f
i
+
5
∘
f
i
−
1
∘
f
i
+
1
g_i=f_i\circ f_{i-5}\circ f_{i+5}\circ f_{i-1}\circ f_{i+1}
gi=fi∘fi−5∘fi+5∘fi−1∘fi+1,
化为矩阵形式为: (
g
1
,
g
2
,
g
3
,
.
.
.
g
25
g_1,g_2,g_3,...g_{25}
g1,g2,g3,...g25)=(
f
1
,
f
2
,
f
3
,
.
.
.
f
25
f_1,f_2,f_3,...f_{25}
f1,f2,f3,...f25)
A
25
∗
25
A_{25*25}
A25∗25 …[1]
(由上式可知
A
25
∗
25
A_{25*25}
A25∗25第i列只有第i-1,i-5,i+5,i+1,i-1行是1.可以根据这个写一下A,发现A可以表示成以下形式:
A
=
H
E
O
O
O
E
H
E
O
O
O
E
H
E
O
O
O
E
H
E
O
O
O
E
H
(1)
A= \begin{matrix} H & E& O& O& O \\ E & H & E & O& O\\ O & E & H& E& O\\ O &O& E & H& E\\ O & O& O& E& H \end{matrix} \tag{1}
A=HEOOOEHEOOOEHEOOOEHEOOOEH(1) 其中每个矩阵都是5*5,E为单位矩阵,O为零矩阵,
H
=
1
1
0
0
0
1
1
1
0
0
0
1
1
1
0
0
0
1
1
1
0
0
0
1
1
(1)
H= \begin{matrix} 1 & 1& 0& 0& 0 \\ 1 & 1 & 1 & 0& 0\\ 0 & 1 & 1& 1& 0\\ 0 &0& 1 & 1& 1\\ 0 &0& 0 & 1& 1 \end{matrix} \tag{1}
H=1100011100011100011100011(1)将[1]代入得: ((
f
1
,
f
2
,
f
3
,
.
.
.
f
25
f_1,f_2,f_3,...f_{25}
f1,f2,f3,...f25)
A
25
∗
25
A_{25*25}
A25∗25x)(
s
0
s_0
s0)=
s
1
s_1
s1
而(
f
1
,
f
2
,
f
3
,
.
.
.
f
25
f_1,f_2,f_3,...f_{25}
f1,f2,f3,...f25)
(
1
,
1
,
1
,
.
.
.
,
1
)
T
(1,1,1,...,1)^T
(1,1,1,...,1)T=
s
1
s_1
s1,故
A
25
∗
25
A_{25*25}
A25∗25x=
(
1
,
1
,
1
,
.
.
.
,
1
)
T
(1,1,1,...,1)^T
(1,1,1,...,1)T
当这个方程有解,这些灯才能由暗全部亮起,否则不可能达到目标,至于解方程,注意因为这个x向量只能由0,1组成,所以解方程时不能直接走常规路线,具体可以参考抽象代数的书籍(笔者能力有限,请谅解)。
这是我写的第一篇文章,有很多不足之处,希望大家多多理解,不当之处请指正,谢谢!最后希望读者可以从这篇文章体会细心线性代数的应用,理解思想,加油!