醉汉看守

醉汉看守是这样的一个问题:有N个上了锁的牢记。看守要喝N杯威士忌,每喝一杯他就执行一个这样的操作:按规则 R 遍历所有的牢房,如果门是开着的就锁上,如果门是锁着的就打开。遍历规则 R 是:喝第 i 杯威士忌时,仅遍历 i , 2*i , 3 * i , .... 牢房。问,在他喝完N杯威士忌后,有多少个牢房是开着的?


模拟这个过程就是,第一次喝酒后,所有牢房都开了;第二次喝酒后,2,4,6,8,10...牢房与上一次相反,其它牢房维持上一次状态;第三次喝酒后,3,6,9牢房与上一次相反,其它牢房维持一致;........

每一个牢房在N次操作后是否开着,取决于它所有小于N的因子(包括1和自身)的个数,若个数为奇数,则它与初始状态相反,即开着,如果个数为偶数,则它与初始状态相同,即关着。

每一个数字存在一个因子,必有另一个因子,即因子的出现总是成对的。只有一个数字为平方数时才不是成对出现的,因为两个因子都是它,所以只能算一个。将这个结论运用到上面的模型中就是,对于编号为 i 的牢房,在第 q 次操作时会遍历到它,这个 q 是 i 的因子。设 i = p * q,则不仅第 q 次操作会遍历到 i 号牢房,而且第 p 次操作也会遍历到 i 号牢房,这两次操作的作用被抵消了(关关为开,开开为关)。而若 i 还存在一对相同的因子a,即 i = a * a,则第 a 次操作会改变 i 号牢房,而且只会被改变一次!


所以,问题转化为,找出比 N 小的所有正整数里面平方数的个数即可。使用 floor(sqrt(N))即能得到结果。


普通的解法是维护一个 N 维的数组,0表示关,1表示开,有两层循环来模拟醉汉模型即可得出最终结果,核心循环为:


int state[N] ;

memset(state,0,N);

for(int i = 0;i < N;i ++)

{

for (int j = i;j < N;j += j)

{

 state[j]  = 1 - state[j];

}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值