首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

經典算法入門:開燈問題 (Locker Problem) C 語言完整解析

     編輯:關於C語言

問題描述

有 n 盞燈,初始全滅。k 個人依次操作:第 i 個人 (1 ≤ i ≤ k) 將編號為 i 的倍數的燈切換狀態 (開→關,關→開)。最終輸出亮著的燈編號。

即:燈 j 被切換的次數等於 j 在 [1, k] 範圍內的約數個數。奇數次亮,偶數次滅。

數學洞察:完全平方數

約數通常成對出現 (d, j/d),只有當 j = d² 時約數不成對 (d 自身配對)。因此:

最終亮燈的編號,恰好是不超過 min(n, k) 的完全平方數。

若 k ≥ n,答案就是 1, 4, 9, 16, ... ≤ n。若 k < n,則為 1, 4, 9, ... ≤ k 且平方數 ≤ n。

算法演進:從模擬到 O(1) 輸出

版本 1:暴力模擬 (教學用)

#include 
#include 
#include 

int main() {
    int n, k;
    while (scanf("%d %d", &n, &k) == 2) {
        bool *lamp = calloc(n + 1, sizeof(bool)); // 索引 1..n
        for (int i = 1; i <= k; ++i)
            for (int j = i; j <= n; j += i)
                lamp[j] = !lamp[j];
        bool first = true;
        for (int j = 1; j <= n; ++j)
            if (lamp[j]) {
                if (!first) putchar(' ');
                printf("%d", j);
                first = false;
            }
        puts("");
        free(lamp);
    }
    return 0;
}

時間 O(n log n) (調和級數),空間 O(n)。適合 n ≤ 10⁵。

版本 2:數學優化 O(√n) — 直接輸出平方數

#include 
#include 

int main() {
    int n, k;
    while (scanf("%d %d", &n, &k) == 2) {
        int limit = (k < n) ? k : n;
        int max_root = (int)sqrt(limit);
        for (int i = 1; i <= max_root; ++i) {
            int sq = i * i;
            if (sq > n) break; // 當 k > n 時需額外判斷
            if (i > 1) putchar(' ');
            printf("%d", sq);
        }
        puts("");
    }
    return 0;
}

時間 O(√min(n,k)),空間 O(1)。極快,支持 n, k 至 10¹²+ (用 64 位整數)。

版本 3:位運算技巧 (無 sqrt 库)

int max_root = 0;
while ((max_root + 1) * (max_root + 1) <= limit) ++max_root;

邊界情況與測試用例

nk輸出說明
10101 4 9完全平方數 ≤ 10
1031k=3, 僅 1²=1 ≤ 3 且 ≤ 10
10010001 4 9 16 25 36 49 64 81 100k>n, 受限於 n
111最小規模
05(空行)n=0 邊界

擴展變體

  1. 圓圈燈:編號環狀,約數邏輯不變。
  2. 多輪操作:k 輪後再反向操作,等價於模 2 計數。
  3. 輸出狀態數組:需保留 bool 數組版本。
  4. 大數 (BigInt):Python/Java 直接用 i*i 循環,C 需手寫大整數或用 GMP。

複雜度總結

方法時間空間適用場景
雙循環模擬O(n log n)O(n)教學演示、小規模、需完整過程記錄
平方數直接輸出O(√n)O(1)競賽/面試/生產、超大規模、極致性能

核心代碼模板 (競賽級)

#include 
#include 

int main() {
    long long n, k;
    while (scanf("%lld %lld", &n, &k) == 2) {
        long long limit = k < n ? k : n;
        long long r = sqrt((double)limit);
        while ((r+1)*(r+1) <= limit) ++r; // 修正浮點誤差
        while (r*r > limit) --r;
        for (long long i = 1; i <= r; ++i) {
            if (i > 1) putchar(' ');
            printf("%lld", i*i);
        }
        putchar('\n');
    }
    return 0;
}

總結

開燈問題是數論洞察力的經典考察:從 O(n log n) 模擬到 O(√n) 數學解法,體現了「先思考數學本質,再寫代碼」的工程智慧。面試或競賽中,直接給出平方數解法並說明約數成對論證,是滿分答案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved