หม้อวิเศษ (pot)

1079

ข้อนี้ต้องถามว่าหากต้องแบ่งหุงข้าว NN จาน โดยแต่ละครั้งหุงได้เกิน KK จานต่อครั้ง จะสามารถแบ่งหุงจนครบ NN ได้กี่วิธี

สำหรับข้อนี้วิธีแรกที่เห็นได้ง่ายที่สุดคือ Dynamic Programming โดยเห็นได้ว่า dp[i]=Σj=max(0,ik)idp[j]dp[i] = \Sigma_{j=\max(0,i-k)}^i dp[j] (ในการหุงครั้งจากต้องการ ii จะเหลือต้องการ jj จานโดย ikji1i-k\leq j\leq i-1)

แต่เนื่องจากข้อนี้กำหนด N100000,K100000N \leq 100000, K \leq 100000 วิธีนี้ที่ใช้เวลา O(NK)\mathcal{O}(NK) จึงช้าเกินไป เราจึงต้องใช้วิธีที่เร็วกว่านี้

เราสามารถกำหนด C[i]=Σj=0idp[j]C[i] = \Sigma_{j=0}^i dp[j] ซึ่งจะทำให้สามารถคำนวณ dp[i]=C[i1]dp[i] = C[i-1] เมื่อ ik1<0i-k-1 < 0 และ dp[i]=C[i1]C[ik1]dp[i] = C[i-1] - C[i-k-1] เมื่อ ik10i-k-1 \geq 0 ในเวลา O(1)\mathcal{O}(1) (C[i]C[i] สามารถคำนวณเป็น C[i]=C[i1]+dp[i]C[i]=C[i-1] + dp[i] ในเวลา O(1)\mathcal{O}(1) เช่นกัน)

ดังนั้นจึงต้องใช้เวลาเพียง O(1)\mathcal{O}(1) สำหรับการคำนวณ C[i]C[i] และ dp[i]dp[i] สำหรับแต่ละ iNi \leq N ทั้งหมดจึงใช้เวลา O(N)\mathcal{O}(N)

โค้ดประกอบคำอธิบาย

    dp[0] = 1;
    C[0] = 1;
    for(int i=1;i<=n;i++)
    {
        if (i-k-1>=0)
            dp[i] = C[i-1] - C[i-k-1];
        else
            dp[i] = C[i-1];
        dp[i] = dp[i] %2009;
        if(dp[i]<0)
            dp[i]+=2009;
        C[i] = C[i-1] + dp[i] %2009;
    }