题解:学而思编程 无法入睡

【题目来源】

学而思编程:无法入睡

【题目描述】

\(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

【核心思想】

  1. 问题分析:给定 \(n\) 个房间,每个房间亮灯状态为 \(a_i\)\(1\) 亮灯,\(0\) 关灯)。如果某个房间关着灯(\(a_i=0\))但左右都亮着灯(\(a_{i-1}=a_{i+1}=1\)),则该房间的人受打扰。求最少关掉多少房间的灯,使所有关着灯的房间都不受打扰。这是一个贪心扫描问题,关键在于从左到右扫描时,每次遇到打扰情况优先关掉右边的灯。

  2. 算法选择

    • 贪心扫描策略:从左到右遍历房间,当发现当前房间受打扰时,选择关掉右边的灯
    • 局部最优选择:关掉右边的灯可以同时消除当前位置的打扰,并可能影响后续位置,具有"一石二鸟"的效果
    • 线性遍历:只需一次遍历,时间复杂度 \(O(n)\)
  3. 关键步骤

    • 读取输入\(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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),只需一次线性扫描
    • 空间复杂度:\(O(n)\),存储房间状态数组
  5. 贪心扫描的核心思想

    • 局部最优性:每次遇到打扰情况,选择关掉右边的灯,这样可以同时解决当前问题并可能预防后续问题
    • 无后效性:关掉右边的灯不会影响已经处理过的左边区域,保证贪心选择的正确性
    • 线性处理:从左到右一次遍历即可得到最优解,无需回溯
    • 边界处理:第一个和最后一个房间不可能被夹在中间,所以从 \(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
posted @ 2026-06-14 10:46  团爸讲算法  阅读(10)  评论(0)    收藏  举报