:-: 16.2 [灯泡开关](https://leetcode-cn.com/problems/bulb-switcher)
*****
**题干:**
初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。
示例:
~~~
输入: 3
输出: 1
解释:
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭].
你应该返回 1,因为只有一个灯泡还亮着。
~~~
**题目分析:**
这种明显是一种找规律的题目,最简单的做法就是按照题意强行来解,也是最容易超时的一种方法,可以试试,超出时间限制的话就不要强求了。我们先根据题意来分析一下,求的是*n* 轮后有多少个亮着的灯泡。我们应该明确一个事情,灯泡一开始都是关闭的,如果某一个灯泡最后是亮的,则**肯定是被按了奇数次开关**。我们找一下被按了奇数次开关都是第几个灯泡(按照第一种思路来写个程序,找出来就行)。
我们假设只有20个灯泡,根据我们写的程序的输出为\[true, false, false, true, false, false, false, false, true, false, false, false, false, false, false, true, false, false, false, false\],发现1,4,9,16位置的为true,它们的共同特性就是**完全平方数**(这里已经可以着手解题了,统计完全平方的个数呗),这就让我们纳闷了?为什么只有位置是完全平方数的灯泡被按了奇数次?
我们假设只有6个灯泡,根据我们的程序输出一下中间过程,看看能否发现什么规律?
1:true true true true true true
2:true false true false true false
3:true false false false true true
4:true false false true true true
5:true false false true false true
6:true false false true false false
第一个灯泡一直没有变过,看第四个灯泡,在第1轮,第2轮,第4轮被按了;看第六个灯泡第1轮会被按,第2轮,第3轮,第6轮都会被按。我们由此可以分析看出 4 = 1 \* 4 = 2 \* 2,6 = 1 \* 6 = 2 \* 3。由此我们看出来原来是看因子个数啊,但是一般因子都是成双结对,只有完全平方数是某个整数的平方的形式,此时的因子是一个,比如4 = 2 \* 2。
于是经过这么多步骤,我们的问题就变成了1~n中有几个完全平方数,我们可以根据[判断一个数是否为平方数](https://blog.csdn.net/puqutogether/article/details/42098609)的方法统计完全平方数个数,但是还是太复杂了些。其实我们求1~n中完全平方数的个数,只需要将n开方向下取整即可。
**新手有可能遇到的解题思路陷阱:**
沉迷于暴力法优化不可自拔,对因子不够熟悉。
**解题思路分析以及代码实现:**
第一种思路:暴力法。
第一种思路代码:
~~~
public static int bulbSwitch(int n) {
boolean[] bulb = new boolean[n];
int i = 0;
while (i <= n) {
for (int j = i; j < n; j += (i + 1)) {
bulb[j] = !bulb[j];
}
i++;
}
n = 0;
for (boolean sign : bulb) {
if (sign)
n++;
}
return n;
}
~~~
**复杂度分析:**
时间复杂度:O(n^2)。
空间复杂度:O(n)。
第二种思路:求完全平方数个数。
第二种思路代码:
~~~
public static int bulbSwitch(int n) {
return (int) Math.sqrt(n);
}
~~~
**若有理解错误的、写错的地方、更好的思路,方法,望各位读者评论或者发邮件指正或指点我,不胜感激。**
- 前言
- 第一部分 初级入门算法
- 第一章 数组
- 1.1 删除排序数组中的重复项
- 1.2 删除排序数组中的重复项 II
- 1.3 买卖股票的最佳时机
- 1.4 买卖股票的最佳时机 II
- 1.5 移动零
- 1.6 区间子数组个数
- 1.7 搜索插入位置
- 1.8 合并两个有序数组
- 1.9 两个数组的交集
- 第二章 哈希表
- 2.1 两数之和
- 2.2 错误的集合
- 2.3 翻转卡片游戏
- 2.4 有效的字母异位词
- 第三章 链表
- 第四章 数学
- 4.1 加一
- 4.2 反转整数
- 4.3 排列硬币
- 4.4 完全平方数
- 4.5 消除游戏
- 第五章 双指针
- 第六章 字符串
- 6.1 整数转罗马数字
- 6.2 罗马数字转整数
- 6.3 反转字符串
- 6.4 压缩字符串
- 6.5 验证回文串
- 6.6 长按键入
- 6.7 字符串中的第一个唯一字符
- 第七章 二分查找
- 7.1 猜数字大小
- 第八章 分治算法
- 第九章 动态规划
- 9.1 爬楼梯
- 9.2 使用最小花费爬楼梯
- 9.3 打家劫舍
- 9.4 打家劫舍 II
- 9.5 第N个泰波那契数
- 第十章 回溯算法
- 第十一章 栈
- 11.1 棒球比赛
- 第十二章 堆
- 12.1 数组中的第K个最大元素
- 第十三章 贪心算法
- 第十四章 排序
- 14.1 冒泡排序
- 14.2 鸡尾酒排序
- 14.3 选择排序
- 14.4 插入排序
- 14.5 折半插入排序
- 14.6 希尔排序
- 14.7 快速排序
- 14.8 树形选择排序
- 14.9 堆排序
- 第十五章 位运算
- 15.1 只出现一次的数字
- 第十六章 思维题
- 16.1 TinyURL 的加密与解密
- 16.2 灯泡开关
- 16.3 字母板上的路径
- 第十七章 队列
- 17.1 扑克牌顺序