题解:学而思编程 无法入睡
【题目来源】
学而思编程:无法入睡
【题目描述】
有 \(n\) 间房间排成一排,有些房间亮着灯,有些房间关着灯。
如果一个房间关着灯,但是旁边两个房间都亮着灯,那么这个房间的人就会受到灯光打扰无法入睡。
用 \(a_i\) 表示第 \(i\) 个房间是否开着灯,对于 \(2≤i≤n−1\),如果 \(a_i=0\) 且 \(a_{i−1}=a_{i+1}=1\),那么第 \(i\) 个房间的人就受到打扰。
现在给出哪些房间开着灯,问最少关掉多少房间的灯,就可以让所有关着灯的房间的人不受到打扰。
【输入】
第 \(1\) 行,\(1\) 个正整数 \(n\)
第 \(2\) 行,\(n\) 个整数 \(a_1,a_2,⋯,a_n\)。
\(a_i=0\) 表示第 \(i\) 个房间关着灯,\(a_i=1\) 表示第 \(i\) 个房间开着灯。
【输出】
输出一个整数表示答案
【输入样例】
10
1 1 0 1 1 0 1 0 1 0
【输出样例】
2
【核心思想】
-
问题分析:给定 \(n\) 个房间,每个房间亮灯状态为 \(a_i\)(\(1\) 亮灯,\(0\) 关灯)。如果某个房间关着灯(\(a_i=0\))但左右都亮着灯(\(a_{i-1}=a_{i+1}=1\)),则该房间的人受打扰。求最少关掉多少房间的灯,使所有关着灯的房间都不受打扰。这是一个贪心扫描问题,关键在于从左到右扫描时,每次遇到打扰情况优先关掉右边的灯。
-
算法选择:
- 贪心扫描策略:从左到右遍历房间,当发现当前房间受打扰时,选择关掉右边的灯
- 局部最优选择:关掉右边的灯可以同时消除当前位置的打扰,并可能影响后续位置,具有"一石二鸟"的效果
- 线性遍历:只需一次遍历,时间复杂度 \(O(n)\)
-
关键步骤:
- 读取输入:\(n\)(房间数量)、\(a[1..n]\)(每个房间的亮灯状态)
- 贪心扫描(遍历 \(i\) 从 \(2\) 到 \(n-1\)):
- 检查打扰条件:如果 \(a_{i-1}=1\) 且 \(a_i=0\) 且 \(a_{i+1}=1\)
- 执行关灯:将 \(a_{i+1}\) 设为 \(0\)(关掉右边房间的灯)
- 计数:
cnt++
- 输出结果:最少关灯数量 \(cnt\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),只需一次线性扫描
- 空间复杂度:\(O(n)\),存储房间状态数组
-
贪心扫描的核心思想:
- 局部最优性:每次遇到打扰情况,选择关掉右边的灯,这样可以同时解决当前问题并可能预防后续问题
- 无后效性:关掉右边的灯不会影响已经处理过的左边区域,保证贪心选择的正确性
- 线性处理:从左到右一次遍历即可得到最优解,无需回溯
- 边界处理:第一个和最后一个房间不可能被夹在中间,所以从 \(i=2\) 遍历到 \(i=n-1\)
- 适用于连续区间处理、相邻元素约束类问题
【算法标签】
贪心
【代码详解】
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n, a[105], cnt; // cnt: 需要关掉的灯的数量
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
// 遍历中间的房间(第一个和最后一个不可能被夹在中间)
for (int i = 2; i < n; i++)
{
// 如果当前房间关着灯,且左右两边都亮着灯,则这个房间的人受到打扰
// 此时需要关掉右边房间的灯来消除干扰
if (a[i - 1] && a[i] == 0 && a[i + 1])
{
a[i + 1] = 0; // 关掉右边房间的灯
cnt++; // 记录关灯次数
}
}
cout << cnt;
return 0;
}
【运行结果】
10
1 1 0 1 1 0 1 0 1 0
2
浙公网安备 33010602011771号