Question1
用動態規划方法手工求解下面的問題:
某工廠調查了解市場情況,估計在今后四個月內,市場對其產品的需求量如下表所示。
時期(月) |
需要量(產品單位) |
1 2 3 4 |
2 3 2 4 |
已知:對每個月來講,生產一批產品的固定成本費為 3 (千元),若不生產,則為零。每生產單位產品的成本費為 1 (千元)。同時,在任何一個月內,生產能力所允許的最大生產批量為不超過6個單位。又知每單位產品的庫存費用為每月 0.5 (千元),同時要求在第一個月開始之初, 及在第四個月末,均無產品庫存。 問:在滿足上述條件下,該廠應如何安排各個時期的生產與庫存,使所花的總成本費用最低?
要求:寫出各種變量、狀態轉移方程、遞推關系式、和詳細計算步驟。
Solution:
階段:按月份時間進行階段划分,i表示第i月
狀態:月初時的庫存量S
決策集合:第i月生產單位產品的數量ki,且0<=ki<=6
不妨設第i月的產品需求量為ai,則狀態間的轉移關系為Si+1 = Si + ki – ai。我們設F[i , s]為從第i月到第n(n=4)月的最低總成本費用,則不難得出如下狀態轉移方程的遞推關系式:
其中:w=0 (k==0) 或 w=3 + 1*k (1<=k<=6)
邊界條件:F[5,0] = 0;s + k –a[i] >=0;
目標結果狀態:F[1,0]即所求最低成本費用
手工求解計算詳細計算步驟如下:
i=4 |
|||||||||
狀態 |
k=0 |
k=1 |
k=2 |
k=3 |
k=4 |
k=5 |
k=6 |
Min |
決策 |
F[4,0] |
N/A |
N/A |
N/A |
N/A |
7 |
8 |
9 |
7 |
4 |
F[4,1] |
N/A |
N/A |
N/A |
6.5 |
7.5 |
8.5 |
9.5 |
6.5 |
3 |
F[4,2] |
N/A |
N/A |
6 |
7 |
8 |
9 |
10 |
6 |
2 |
F[4,3] |
N/A |
5.5 |
6.5 |
7.5 |
8.5 |
9.5 |
10.5 |
5.5 |
1 |
F[4,4] |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
2 |
0 |
i=3 |
|||||||||
狀態 |
k=0 |
k=1 |
k=2 |
k=3 |
k=4 |
k=5 |
k=6 |
Min |
決策 |
F[3 , 0] |
N/A |
N/A |
12 |
12.5 |
13 |
13.5 |
11 |
11 |
6 |
F[3 , 1] |
N/A |
11.5 |
12 |
12.5 |
13 |
10.5 |
N/A |
10.5 |
5 |
F[3 , 2] |
8 |
11.5 |
12 |
12.5 |
10 |
N/A |
N/A |
8 |
0 |
F[3 , 3] |
8 |
11.5 |
12 |
9.5 |
N/A |
N/A |
N/A |
8 |
0 |
F[3 , 4] |
8 |
11.5 |
9 |
N/A |
N/A |
N/A |
N/A |
8 |
0 |
F[3 , 5] |
8 |
8.5 |
N/A |
N/A |
N/A |
N/A |
N/A |
8 |
0 |
F[3 , 6] |
5 |
N/A |
N/A |
N/A |
N/A |
N/A |
N/A |
5 |
0 |
i=2 |
|||||||||
狀態 |
k=0 |
k=1 |
k=2 |
k=3 |
k=4 |
k=5 |
k=6 |
Min |
決策 |
F[2 , 0] |
N/A |
N/A |
N/A |
17 |
17.5 |
16 |
17 |
16 |
5 |
F[2 , 1] |
N/A |
N/A |
16.5 |
17 |
15.5 |
16.5 |
17.5 |
15.5 |
4 |
F[2 , 2] |
N/A |
16 |
16.5 |
15 |
16 |
17 |
18 |
15 |
3 |
F[2 , 3] |
12.5 |
16 |
14.5 |
15.5 |
16.5 |
17.5 |
15.5 |
12.5 |
0 |
F[2 , 4] |
12.5 |
14 |
15 |
16 |
17 |
15 |
N/A |
12.5 |
0 |
F[2 , 5] |
10.5 |
14.5 |
15.5 |
16.5 |
14.5 |
N/A |
N/A |
10.5 |
0 |
F[2 , 6] |
11 |
15 |
16 |
14 |
N/A |
N/A |
N/A |
11 |
0 |
i=1 |
|||||||||
狀態 |
k=0 |
k=1 |
k=2 |
k=3 |
k=4 |
k=5 |
k=6 |
Min |
決策 |
F[1,0] |
N/A |
N/A |
21 |
21.5 |
22 |
20.5 |
21.5 |
20.5 |
5 |
由上表不難得出,所花的總成本費用最低為20.5(千元),該情況下的由遞推公式逆推可得決策安排如下:第一個月生產5個單位產品,第二個月生產0個,第三個月生產0個,第四個月生產6個,該方案可使總成本最低,即20.5(千元)。
源代碼:
#include<iostream> using namespace std; double f[5][7] = {0}; int a[5] = {0,2,3,2,4}; int main() { for(int i=0;i<5;i++) for(int j=0;j<=7;j++) f[i][j] = 1000; f[5][0] = 0; for (int i=4;i>=1;i--){ for(int s=0;s<=6;s++) { int temp = 0; for(int k=i;k<=4;k++) temp+= a[k]; if (s>temp) continue; double min = 10000; int u = -1; for(int j=0;j<=6;j++){ int w = 3 + j; if (j==0) w = 0; if (s + j - a[i] >= 0 && s + j - a[i] <=6) { if (min>f[i+1][s+j-a[i]] + w + 0.5*s) { min = f[i+1][s+j-a[i]] + w + 0.5*s; u = j; } } } f[i][s] = min; } } cout<<"Answer:"<<f[1][0]<<endl; return 0; }
Question2:
用動態規划方法編程求解下面的問題:
某推銷員要從城市 v1出發,訪問其它城市 v2,v3,…,v6各一次且僅一次,最后返回 v1。D 為各城市間的距離矩陣。
問:該推銷員應如何選擇路線,才能使總的行程最短?
要求:寫出遞推關系式、偽代碼和程序相關說明,並分析時間復雜性。(請遵守第一節課提出的有關 assignment 的要求)
Solution:
設F[i,s]表示當前所在節點為i,已經走過的節點集合為s的最短路程。決策為選擇下一個節點k,因此,的狀態轉移方程的遞推關系式:
其中,d[i,k]表示i節點到k節點的距離
源代碼:
一些說明:有幾個關鍵問題需要說明
1、集合如何表示,表示集合有個很好的方法,就是使用二進制模型。例如:11101表示含有1、3、4、5元素的集合。這一一個十進制的數字就可以代表一個集合。
2、那么如何進行對集合的操作呢?對於位運算,我們可以利用1的左右移(<< or >>)來判斷是否包含某個元素。S-{k}也就是方便的表示為:S[j] & (length-(1<<(k-1))),其中length=(1 << (n-1)) - 1;
3、該圖表示一個求解模型樹,不難發現含有一個元素的集合要先進行計算,才可以計算還有兩個元素狀態的解空間,這樣就要求不同集合之間是有序的,即含有二進制1的個數少的要排在前面。這樣就不會在計算過程中,出現使用未計算結果的情況。實現方法可以使用預排序。因為排序的復雜度相對於整體算法的復雜性而言,是很小的,不會過多影響性能。
時間復雜度:
由於集合的狀態個數為2n-1 個,還需要枚舉每個結點,以及每個集合中的元素,因此整個算法的近似復雜度為O(n2*2n)。
最后給出源代碼(水平有限僅供參考):
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxnum = 100001; int Count(int v){ int num = 0; while(v){ v &= (v-1); num ++; } return num; } bool cmp(int a , int b){ int count1_a = Count(a); int count1_b = Count(b); return count1_a < count1_b; } bool existkey( int key , int S) { int p = 1; p = p << (key-1); p = p & S; if (p>0) return true; else return false; } void getElements( int S , int * arr , int length , int &return_len) { int p = 1; int pos = 0; for(int i=0;i<length;i++){ if (p == (p&S)){ arr[pos++] = i+1; } p = p << 1; } return_len = pos; } int main() { freopen("input.txt","r",stdin); int n = 0 ; cin >> n; int F[10][100]; int S[1000]; int elements [10]; int distance[10][10]; int length_elements = 0; for(int i=0 ; i<n ; i++ ){ for (int j=0 ; j<n ; j++) { cin>>distance[i][j]; } } for (int i=1 ; i<=n ;i++) F[i][0] = distance[i][0]; int length = (1 << (n-1)) - 1; for (int i=1 ; i<=length ; i++){ S[i] = i; } sort(S,S+length,cmp); S[0] = 0; for (int j=1 ; j<=length ; j++) { for(int i=1 ; i<n ; i++) { if (existkey(i , S[j]) != true){ getElements(S[j] , elements, n , length_elements); int min = maxnum; for (int p=0 ; p<length_elements ; p++){ int k = elements[p]; int jj = S[j] & (length-(1<<(k-1))) ; if (min > ( F[k][jj] + distance[i][k] ) && k!=i ) min = F[k][jj] + distance[i][k]; } F[i][S[j]] = min; } } } int ans = maxnum; for (int i=1 ; i<n ; i++){ int j = length & (length -(1<<(i-1))); if (ans > F[i][j] + distance[0][i]) ans = F[i][j] + distance[0][i]; } cout<<"Answer:\t"<<ans<<endl; return 0; }