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

C語言動態規劃解題:HDU 2709 Sumsets 完整解析與兩種優化策略

     編輯:關於C語言

C語言動態規劃實戰:HDU 2709 Sumsets 完整解析

本題要求計算正整數 N (1 ≤ N ≤ 1,000,000) 表示為 2 的冪次數之和的方法數,僅輸出最後 9 位數字。這是經典的整數分割變體問題,展示動態規劃狀態轉移設計與空間優化技巧。

問題數學建模

令 a[n] 表示整數 n 的合法分割數。加數限制為 2^k (k≥0)。觀察奇偶性規律:

  • n 為奇數:必含至少一個 1,移除一個 1 後剩餘 n-1 為偶數,分割數等於 a[n-1]
  • n 為偶數:分兩種情況
    1. 含 1:必含至少兩個 1,等價於 n-2 的分割數 a[n-2]
    2. 不含 1:所有加數均為偶數,除以 2 後對應 n/2 的分割數 a[n/2]

遞推公式:

a[1] = 1; a[2] = 2;
for n = 3 to N:
    if n % 2 == 1: a[n] = a[n-1]
    else:          a[n] = (a[n-2] + a[n/2]) % 1000000000

解法一:線性遞推 O(N) 時間 O(N) 空間

#include <stdio.h>
#include <stdint.h>

#define MOD 1000000000
#define MAXN 1000000

int64_t dp[MAXN + 1];

int main() {
    dp[1] = 1;
    dp[2] = 2;
    for (int i = 3; i <= MAXN; i++) {
        if (i & 1) dp[i] = dp[i-1];
        else dp[i] = (dp[i-2] + dp[i/2]) % MOD;
    }

    int64_t n;
    while (scanf("%lld", &n) == 1) {
        printf("%lld\n", dp[n]);
    }
    return 0;
}

複雜度:預計算 O(MAXN),查詢 O(1)。適用於多測試案例場景。

解法二:完全背包 DP O(N log N) 時間 O(N) 空間

視為硬幣找零問題:硬幣面值為 1, 2, 4, 8, ..., 2^24 (2^24 > 10^6),每種無限供應,求組合數 (順序不計)。

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define MOD 1000000000
#define MAXN 1000000

int64_t dp[MAXN + 1];
int coins[25];

int main() {
    // 初始化 2 的冪次硬幣
    coins[0] = 1;
    for (int i = 1; i < 25; i++) coins[i] = coins[i-1] << 1;

    int n;
    while (scanf("%d", &n) == 1) {
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;  // 和為 0 有 1 種方法 (不選任何硬幣)

        for (int i = 0; i < 25 && coins[i] <= n; i++) {
            for (int j = coins[i]; j <= n; j++) {
                dp[j] = (dp[j] + dp[j - coins[i]]) % MOD;
            }
        }
        printf("%lld\n", dp[n]);
    }
    return 0;
}

狀態轉移:dp[j] += dp[j - coin] 代表「使用當前硬幣」的方案數累加。外層遍歷硬幣、內層遍歷金額確保組合不重複計數。

兩種解法對比

維度解法一 (遞推)解法二 (背包)
時間複雜度O(N) 預計算O(N log N) 每查詢
空間複雜度O(N)O(N)
多查詢優勢極大 (預計算一次)需重複計算
數學洞察需推導奇偶規律通用背包模型
擴充性難以推廣易修改硬幣集合

關鍵實作細節

  1. 資料型別:使用 int64_t / long long 防止中間結果溢位
  2. 模運算時機:每次加法後立即取模,避免溢位
  3. 輸入處理:while (scanf(...) == 1) 處理多測試案例至 EOF
  4. 邊界條件:dp[0] = 1 為背包 DP 正確性關鍵

延伸思考

  • 若需輸出完整結果 (超過 9 位),需實作大整數加法或使用 GMP 庫
  • 生成函數視角:∏_{k≥0} 1/(1-x^{2^k}) 的係數即為答案
  • 該數列為 OEIS A000123 (二進制分割數)

本題展示從數學規律推導優化遞推與通用背包模型兩種思維路徑。實戰中根據題目約束 (單查詢/多查詢、N 範圍、時間限制) 靈活選擇策略,是演算法競賽與工程優化的核心能力。

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