程序员的算法趣题: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 被翻的牌 规律
1 2 2,4,6,8,10,12,... 2的倍数
2 3 3,6,9,12,15,18,... 3的倍数
3 4 4,8,12,16,20,24,... 4的倍数
4 5 5,10,15,20,25,30,... 5的倍数
5 6 6,12,18,24,30,36,... 6的倍数
6 7 7,14,21,28,35,42,... 7的倍数
x x+1 ———— (x+1)的倍数

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

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

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

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


 

你可能感兴趣的:(程序员的算法趣题,算法,java)