一个门上有16个把手,并组成4x4的矩阵,把手有开有关,当把手所有都处于打开状态,则门就打开了。但是改变一个把手的状态,那么把手所在的行和列的所有把手的状态全部都改变。
当给定把手的初始状态后,求出最少需要改变多少个把手的状态就可以将门打开。
根据网上提示,这个题目使用枚举,所以我也就直接使用的枚举。
简单思路
要想获得最少修改把手的个数,我们从1开始枚举,将所有把手改变一次,验证是否可以将门打开,如果能,则表明最少需要1次,如果不能,则验证2是否能够将门打开,以此类推,直到将门打开。
循环验证一个或者多个把手,很自然想到使用递归办法,(关于通用递归方法剖析,请查看我对递归的分析,http://blog.csdn.net/gykimo/article/details/8449131)。本题我的递归思路如下:
要想验证改变N个把手的状态是否可以将门打开,则首先应该将第一个把手改变状态,然后在余下的15个把手里面验证改变N-1个把手状态是否可以将门打开,如果能,则停止,如果不能,则将第2个把手改变状态,然后在余下的14个把手里面验证改变N-1个把手状态是否可以将门打开,以此类推。更一般的说明应该如下,假设现在已经在前p个把手里面改变了m(m<=p)个把手的状态,并且m把手是第p 个把手。验证在余下16-p个把手里面改变N-m个把手的状态是否可以将门打开,如果能,则停止,如果不能,则将第p+1个把手设为要改变的第m个把手,然后验证在余下16-p-1个把手里面改变N-m个把手的状态是否可以将门打开,依此类推。
数据结构
matrix[4][4] :把手矩阵,共16个
switchnum :需要改变状态把手的个数
递归函数
bool exe(int handle_index, inthandle_location)
handle_index :第几个需要改变状态的把手(1-switchnum)
handle_location :第handle_index个把手在所有把手中的位置(0-15)
改变第handleIndex及后面switchnum-handleIndex个把手后是否能够将门,如果打开返回true,如果不能打开,返回false
递归步骤
1. 设置阀值
第handle_index个把手位置是否有效,判断方法: handle_location + (switchnum - handle_index) > 15
如果位置无效,则返回false。
2. 处理
保存当前把手矩阵状态,因为如果当第handleIndex把手在handleLocation位置无法将门打开,则应该将把手矩阵状态恢复到未尝试开启时的状态。
改变handleIndex把手状态,也就是改变handleLocation位置的把手状态。
3. 递归
如果handleIndex把手后面还有把手,改变handleIndex把手后面的把手状态
4. 合并
如果门已经开启,则返回true
如果门没有开启,并且第handleIndex把手还没有到最后一个有效位置,则将handleIndex把手置为下一个位置。注意,应该将把手矩阵状态恢复到开始状态。
否则,否则返回false
代码
http://download.csdn.net/detail/gykimo/4939694
结果:469MS 688K
上面的算法只是枚举所有情况,没有进行任何剪枝。我们可以分析下这个题目的特点,可以进行部分优化,我们通过分析可以得到以下的定理和推论。
定理一
一个把手最多只能改变一次状态,多次无意义。
证明:
如果把手h改变了2次,那么把手h经过2次改变后,状态未发生变化,根据数学归纳法,很容易证明h改变偶数次状态,h的状态不发生变化,所以h改变偶数次无意义;
如果把手h改变了3次,那么状态和h改变1次状态是一样的,根据数学归纳法,很容易证明h改变多余1次的奇数次状态,h的状态和改变1次的状态一样。
所以,定理成立。
推论1
最多改变16个把手的状态就可以将门打开。
推论2
以任意顺序改变选定的m个把手状态后,16个把手的状态和门的状态是一定的,即和操作的顺序无关。
证明:
改变一个把手,就改变把手所在行和列的所有把手,也就是m个把手选定后,那么某个行和列改变状态的次数是一定的,所以不管m个把手改变顺序如何,最后状态是一定的。证毕。
当然以上的定理和推论对于优化没有任何意思,还有一些现象有助于优化算法,如当只有一行和一列的把手状态为关闭时,改变交点把手,就可以将门打开,其他情况都不能改变一次把手状态就可以将门打开,也可以分析改变2次或者多次把手时,需要什么样的情况,由于没有细致研究其中的规律,所以也就没有对代码进行优化。
另外一个问题,不管初始状态如何,肯定可以将门打开吗?