Algorithm assignment 1

ProboxDu發表於2020-12-10

一、用動態規劃方法手工求解下面的問題:

某工廠調查瞭解市場情況,估計在今後四個月內,市場對其產品的需求量如下表所示。

時期(月) 需要量(產品單位)
1 2
2 3
3 2
4 4

已知:對每個月來講,生產一批產品的固定成本費為3 (千元),若不生產,則為零。每生產單位產品的成本費為1 (千元)。同時,在任何一個月內,生產能力所允許的最大生產批量為不超過6 個單位。又知每單位產品的庫存費用為每月0.5 (千元),同時要求在第一個月開始之初, 及在第四個月末,均無產品庫存。

問:在滿足上述條件下,該廠應如何安排各個時期的生產與庫存,使所花的總成本費用最低?

要求:寫出各種變數、狀態轉移方程、遞推關係式、和詳細計算步驟。


該題既可以順推也可以逆推,但逆推的手工計算量較小。因此這裡用逆推的方式求解。

  • 變數描述:設\(n\) 為總月份,\(w_i\) 代表第\(i\)個月的需求量,\(u_i\) 代表第\(i\)個月的產量,生產固定成本\(c_1=3\) ,生產單位產品成本費\(c_2=1\) ,單位產品的庫存費\(c_3=0.5\) ,單月最大生產量\(max_p=6\)

  • 狀態轉移方程

    \(s_i\)為第i個月的庫存,則\(s_{i+1}=s_i + u_i - w_i\),且\(s_i \geq 0\)\(0 \leq u_i \leq max_p\)

    \(v_i\)為第i個月的花費,則\(v_i = c_3s_i+\begin{cases}c_2u_i+c_1&,&u_i>0\\0&,&u_i=0\end{cases}\)

    \(f_i(s_i)\)為第i個月後,庫存為\(s_i\)的最小成本,則

    \[f_i(s_i)=min_{u_i}\{{v_i + f_{i + 1}(s_{i+1})}\} \]

    最終所求為\(f_1(0)\)

  • 遞推關係式

    \[\begin{cases} f_n(0)=0\\ f_i(s_j)=min_{u_i}\{v_i+f_{i+1}(s_k)\} \end{cases} \]

  • 計算步驟

    由題知,所求\(n = 4; w = \{2,3,2,4\}\),所以有\(u_4=s_5-s_4+w_4=4-s_4\),即\(0 \leq s_4 \leq 4\)

    \[f_4(s_4)=min_{u_4}\{v_4+f_{5}(0)\}=min_{u_4}\{0.5 * s_4+\begin{cases}1 * u_4+3&,&u_4>0\\0&,&u_4=0\end{cases}\} \implies \begin{cases} f_4(0)=7&,&u_4=4\\ f_4(1)=6.5&,&u_4=3\\ f_4(2)=6&,&u_4=2\\ f_4(3)=5.5&,&u_4=1\\ f_4(4)=2&,&u_4=0\\ \end{cases} \]

    所以有\(u_3=s_4-s_3+w_3\),即\(0 \leq s_3 \leq 6\)

    \[f_3(s_3)=min_{u_3}\{v_3+f_{4}(s_4)\}=min_{u_3}\{0.5 * s_3+\begin{cases}1 * u_3+3&,&u_3>0\\0&,&u_3=0\end{cases}\} \implies \begin{cases} f_3(0)=11&,&u_3=6\\ f_3(1)=10.5&,&u_3=6\\ f_3(2)=8&,&u_3=0\\ f_3(3)=8&,&u_3=0\\ f_3(4)=8&,&u_3=0\\ f_3(5)=8&,&u_3=0\\ f_3(6)=5&,&u_3=0\\ \end{cases} \]

    所以有\(u_2=s_3-s_2+w_2\),即\(0 \leq s_2 \leq 9\)

    \[f_2(s_2)=min_{u_2}\{v_2+f_{3}(s_3)\}=min_{u_2}\{0.5 * s_2+\begin{cases}1 * u_2+3&,&u_2>0\\0&,&u_2=0\end{cases}\} \implies \begin{cases} f_2(0)=16&,&u_2=5\\ f_2(1)=15.5&,&u_2=4\\ f_2(2)=15&,&u_2=3\\ f_2(3)=12.5&,&u_2=0\\ f_2(4)=12.5&,&u_2=0\\ f_2(5)=10.5&,&u_2=0\\ f_2(6)=11&,&u_2=0\\ f_2(7)=11.5&,&u_2=0\\ f_2(8)=12&,&u_2=0\\ f_2(9)=9.5&,&u_2=0\\ \end{cases} \]

    \(s_1 = 0\)\(u_1=s_2 - s_1 + w_1\)

    \[f_1(s_1)=min_{u_1}\{v_1+f_{2}(s_2)\}=min_{u_1}\{0.5s_1+\begin{cases}1 * u_1+3&,&u_1>0\\0&,&u_1=0\end{cases}\} \implies f_1(0)=20.5,u_1=5 \]

    \(u=[5,0,6,0]\)\(s = [0,3,0,4]\),即4個月分別生產5、0、6、0單位產品,各月庫存量分別為0、3、0、4,總成本最低為\(f_1(0)=20.5\)

二、用動態規劃方法程式設計求解下面的問題:

某推銷員要從城市\(v_1\) 出發,訪問其它城市\(v_2,v_3,…,v_6\) 各一次且僅一次,最後返回\(v_1\)。D為各城市間的距離矩陣。

\[D= \begin{matrix} v_1 \\ v_2 \\ v_3 \\ v_4 \\ v_5 \\ v_6 \end{matrix} \begin{bmatrix} 0 & 10 & 20 & 30 & 40 & 50\\ 12 & 0 & 18 & 30 & 25 & 21\\ 23 & 19 & 0 & 5 & 10 & 15 \\ 34 & 32 & 4 & 0 & 8 & 16 \\ 45 & 27 & 11 & 10 & 0 & 18 \\ 56 & 22 & 16 & 20 & 12 & 0 \end{bmatrix} \]

問:該推銷員應如何選擇路線,才能使總的行程最短?

要求:寫出遞推關係式、虛擬碼和程式相關說明,並分析時間複雜性。 (請遵守第一節課提出的有關 assignment 的要求)


  • 變數描述:記城市數量\(n=6\),城市編號為\(0,1,2,\dots,n-1\),距離矩陣為\(D\)

  • 狀態轉移方程:設\(f_{i,S}\)代表推銷員走到第i個城市,已經訪問過的城市集合為S,則

    \[f_{i,S}=min\{f_{j,S-\{i\}}+D_{j,i}\} \]

  • 遞推關係式:初始時S為空,\(f_{i,S}=0\)

    \[\begin{cases} f_{i,S}=0\\ f_{i,S}=min\{f_{j,S-\{i\}}+D_{j,i}\} \end{cases} \]

  • 虛擬碼

    func TSP(n, D){
    	// Input: n城市數量,D為距離矩陣下標從0開始
    	// Output: 一個數,代表從v1=0出發,最後回到v1的最小距離
    	初始化f為INF
    	將v1新增進集合S,此時f[i][S]=0
    	從{v1}開始列舉集合S的狀態
    		列舉第i個城市是否在集合內,如果在
    			列舉不在集合內的第j個城市
    				將j加入集合S記為S‘,此時花費為f[i][S] + D[i][j]
    				記f[j][S']=min{f[j][S'], f[i][S] + D[i][j]}
    	則得到f[i][S]為從0出發,最後集合狀態為S的最小
    	最後加上從最後經過的城市返回v1的距離,輸出答案
    }
    
  • 時間複雜度\(O(n^2*2^n)\)

  • 附程式程式碼

    #include<iostream>
    #include<cstring>
    using namespace std;
    #define INF 0x3f3f3f3f
    const int n = 6;
    const int D[6][6]={
        {0,10,20,30,40,50},
        {12,0,18,30,25,21},
        {23,19,0,5,10,15},
        {34,32,4,0,8,16},
        {45,27,11,10,0,18},
        {56,22,16,20,12,0}
    };
    int f[10][1024];
    int main() 
    {
        memset(f, INF, sizeof(f));
        f[0][1] = 0;
        int tot = (1 << n) - 1;
        for (int s = 1; s <= tot; s++) {
            for (int i = 0; i < n; i++) {
                if ((s >> i & 1 == 0) || (f[i][s] == INF)) continue;
                for (int j = 0; j < n; j++) if ((s >> j & 1) == 0)
                    f[j][s | 1 << j] = min(f[j][s | 1 << j], f[i][s] + D[i][j]);
            }
        }
        int ans = INF;
        for (int i = 0; i < n; i++) if (f[i][tot] < INF)
            ans = min(ans, f[i][tot] + D[i][0]);
        cout << ans << endl; 
    }
    

相關文章