程序员的算法趣题:Q03 翻牌(Java版)

题目说明

有 100 张写着数字 1~100 的牌,并按顺序排列着。
最开始所有牌都是背面朝上放置。
某人从第 2 张牌开始,隔 1 张牌翻牌。然后第 2, 4, 6, ..., 100 张牌就会变成正面朝上。
接下来,另一个人从第 3 张牌开始,隔 2 张牌翻牌(原本背面朝上的,翻转成正面朝上;原本正面朝上的,翻转成背面朝上)。
再接下来,又有一个人从第 4 张牌开始,隔 3 张牌翻牌...
像这样,从第 n 张牌开始,每隔 n-1 张牌翻牌,直到没有可翻动的牌为止。
求此时所有背面朝上的牌的数字

思路

1.用boolean[]表示这100张牌,false背面朝上;true正面朝上
2.start表示每一轮翻牌的起始下标;skip表示每一轮隔几张牌(为便于理解,所以用两个变量表示,代码层面可以二合一)
3.翻牌:true变false; false变true

代码

public static void main(String[] args) {
    boolean[] arr = new boolean[100]; // 表示100张牌,默认是false(背面朝上)

    // start表示每一轮翻的第一张牌的下标; skip表示每一轮隔几张牌翻一次
    for(int start = 1, skip = 1; start < arr.length; start ++, skip ++){
        int i = start;
        while(i < arr.length){
            arr[i] = !arr[i]; // 翻牌
            i += skip + 1; // 注意:相隔s张牌,两张牌的下标相差(s+1)。比如1和4,相隔2张牌,下标相差3
        }
        System.out.println("skip = " + skip); // 与start的值相同,该变量可以用start代替
        print(arr); // 自定义的方法print(xx),便于查看每一轮翻牌后的状态
    }

    // 统计最终结果,打印为false的牌的数字
    for(int i = 0; i < arr.length; i ++){
        if(! arr[i]) System.out.print(i+1+" ");
    }
}
// 每十个换一行,输出当前数组的内容
private static void print(boolean[] arr){
    for(int i = 0; i < arr.length; i ++){
        System.out.print((arr[i] ? 1 : 0) + " "); // 1 true;  0 false
        if((i+1)%10==0) System.out.println();
    }
    System.out.println("-------------------");
}

 结果

skip = 1
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
0 1 0 1 0 1 0 1 0 1 
-------------------
skip = 2
0 1 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 0 0 
0 1 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 0 0 
0 1 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 0 0 
0 1 1 1 0 0 0 1 1 1 
-------------------
......省略......
-------------------
skip = 99
0 1 1 0 1 1 1 1 0 1 
1 1 1 1 1 0 1 1 1 1 
1 1 1 1 0 1 1 1 1 1 
1 1 1 1 1 0 1 1 1 1 
1 1 1 1 1 1 1 1 0 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 0 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 0 
-------------------
1 4 9 16 25 36 49 64 81 100 

引申

为啥都是平方数?

1.翻牌次数的奇偶决定了最终正面朝上or背面朝上。(初始背面朝上。翻牌奇数次,正面朝上;翻牌偶数次,背面朝上)
==> 题目转化为:求解翻转了"偶数"次的牌

2.何时翻转?
假设一张牌的数字为n,则当(skip+1)为n的约数时,n会被翻

轮数skip+1被翻的牌规律
122,4,6,8,10,12,...2的倍数
233,6,9,12,15,18,...3的倍数
344,8,12,16,20,24,...4的倍数
455,10,15,20,25,30,...5的倍数
566,12,18,24,30,36,...6的倍数
677,14,21,28,35,42,...7的倍数
xx+1————(x+1)的倍数

比如数字牌12,我们会发现只有当(skip+1)为2,3,4,6,12时,才会被翻牌。而这些数字都是12的约数。
==> 题目转化为:求数字的约数(去掉1后)个数为"偶数"的牌,或者说约数总个数为"奇数"的牌

3.约数总个数为"奇数"的数字都有什么特征?
我们已知:所有约数罗列出来后,首尾两两相乘,得到的就是它本身

数字约数演示备注
91,3,91*9=9;剩3
161,2,4,8,161*16=16; 2*8=16;剩4
251,5,251*25=25;剩5
361,2,3,6,12,18,361*36=36; 2*18=36; 3*12=36;剩6

这些数字本身 = 中间那个约数的平方,所以程序最终结果都是平方数也就不足为奇了。


 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值