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

C 語言動態規劃經典:2 的冪次分拆整數(USACO 3.3 / 諾伊曼數列)

     編輯:關於C

問題描述

將正整數 N (1 ≤ N ≤ 1,000,000) 分拆為若干個 2 的冪次 之和(允許重複),求分拆方案數,輸出最後 9 位。

例:N=7 有 6 種:

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+2+2
1+1+1+4
1+2+2+2
1+2+4

方法一:遞推公式 O(N) —— 推薦

設 dp[n] 為方案數。

  • n 為奇數:必含至少一個 1,去掉兩個 1 等價於 n-2 → dp[n] = dp[n-1](因 dp[n-1]=dp[n-2])
  • n 為偶數:分「含 1」與「不含 1」
    • 含 1:至少兩個 1 → dp[n-2]
    • 不含 1:所有加數乘 2 → 等價 n/2 的分拆 → dp[n/2]

遞推:

dp[1] = 1; dp[2] = 2;
for i = 3..N:
    if i % 2 == 1: dp[i] = dp[i-1];
    else: dp[i] = (dp[i-2] + dp[i/2]) % MOD;

方法二:完全背包 DP O(N log N) —— 通用思路

物品 = 1, 2, 4, 8, ... 2^k ≤ N,每種無限件,求裝滿 N 的方案數。

dp[0] = 1;
for k = 0..24:           // 2^24 = 16,777,216 > 1e6
    coin = 1 << k;
    for i = coin..N:
        dp[i] = (dp[i] + dp[i-coin]) % MOD;

完整代碼(雙版本)

#include 
#include 
#include 

#define MOD 1000000000
#define MAXN 1000000

int64_t dp1[MAXN+1];
int64_t dp2[MAXN+1];
int coins[25];

int main() {
    /* 預計算 2 的冪 */
    coins[0] = 1;
    for (int i = 1; i < 25; i++) coins[i] = coins[i-1] << 1;

    /* 方法一:遞推 */
    dp1[1] = 1; dp1[2] = 2;
    for (int i = 3; i <= MAXN; i++) {
        if (i & 1) dp1[i] = dp1[i-1];
        else dp1[i] = (dp1[i-2] + dp1[i>>1]) % MOD;
    }

    /* 方法二:完全背包 */
    memset(dp2, 0, sizeof(dp2));
    dp2[0] = 1;
    for (int k = 0; k < 25; k++) {
        int c = coins[k];
        if (c > MAXN) break;
        for (int i = c; i <= MAXN; i++)
            dp2[i] = (dp2[i] + dp2[i-c]) % MOD;
    }

    /* 驗證一致性 */
    for (int i = 1; i <= 100; i++)
        if (dp1[i] != dp2[i]) printf("Mismatch at %d\n", i);

    /* 查詢 */
    int n;
    while (scanf("%d", &n) == 1)
        printf("%lld\n", dp1[n]);
    return 0;
}

複雜度對比

方法時間空間優勢
遞推公式O(N)O(N)極快,單次預計算即可答多查詢
完全背包O(N log N)O(N)思路通用,易擴展到其他硬幣系統

關鍵洞見

  1. 奇數必含 1,且 1 的個數為奇數 → 去掉兩個 1 不改變奇偶性
  2. 偶數分「含 1」與「全偶」兩類互不重疊、無遺漏
  3. 模 1e9 只保留低 9 位,防止 64 位溢出(實際 int64_t 足夠)

總結

2 的冪次分拆是動態規劃與數論結合的精美例題。遞推公式法 O(N) 利用奇偶性質將狀態轉移優化至常數,是競賽與工程中處理大規模查詢的首選;完全背包法 則展示了「無限硬幣找零」的通用框架。兩種思路互補,值得深入掌握。

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