裝配線(工作站)問題的兩種解法

2021-08-29 16:38:27 字數 3542 閱讀 7462

上學的時候有一道題目一直困擾著我,那就是廚師擺盤子問題,問題的描述是這樣的:

廚師的所有盤子都放在乙個架子上,每天工作結束他都要將盤子按照從小到大的順序排好,問題是架子不太穩,如果一次拿出乙個或幾個盤子,架子可能要倒掉,所以他必須只能從一邊翻動盤子,由於他只有兩隻手,所以只能用兩隻手將拿起的盤子一起翻轉。問題是當給出乙個雜亂的盤子序列時,如何以最小的翻轉次數將其排序。

colonel汽車公司在有兩條裝配線的工廠內生產汽車,乙個汽車底盤在進入每一條裝配線後,在每個裝配站會在汽車底盤上安裝不同的部件,最後完成的汽車從裝配線的末端離開。如圖1.所示:

圖1. 裝配線示意圖

每一條裝配線上有n個裝配站,編號為j=1,2,...,n,將裝配線i(i為1或2)的第j個裝配站表示為s(i,j)。裝配線1的第j個站s(1,j)和裝配線2的第j個站s(2,j)執行相同的功能。然而這些裝配站是在不同的時間建造的,並且採用了不同的技術,因此,每個站上完成裝配所需要的時間也不相同,即使是在兩條裝配線相同位置的裝配站也是這樣。把每個裝配站上所需要的裝配時間記為a(i,j),並且,底盤進入裝配線i需要的時間為e(i),離開裝配線i需要的時間是x(i)。正常情況下,底盤從一條裝配線的上乙個站移到下乙個站所花費的時間可以忽略,但是偶爾也會將未完成的底盤從一條裝配線的乙個站移到另一條裝配線的下一站,比如遇到緊急訂單的時候。假設將已經通過裝配站s(i,j)的底盤從裝配線i移走所花費的時間為t(i,j),現在的問題是要確定在裝配線1內選擇哪些站以及在裝配線2內選擇哪些站,以使汽車通過工廠的總時間最小,如圖2.所示,最快的時間是選擇裝配線1的1,3和6裝配站以及裝配線2的2,4和5裝配站。

圖2. 裝配線的乙個最快時間路線

按照《演算法導論》書中的討論,我首先給出了使用動態規劃法的演算法。動態規劃的第一步是描述最優解的結構特徵,也就是要先定義什麼是最優解。對於裝配線問題,通過s(i,j)的最優解就是通過s(i,j)的前一站的最優解加上用最短的時間通過s(i,j)站。對於有兩條裝配線的colonel汽車公司來說,通過裝配站s(1,j)的最快路線只能是以下二者之一:

1、通過裝配站s(1,j-1)的最快路線,然後直接通過裝配線s(1,j);

2、通過裝配站s(2,j-1)的最快路線,然後從裝配線2移到裝配線1上,再通過裝配線s(1,j);

同理類推,對於裝配站s(2,j)的最快路線與之對稱相反。由此可以看出,對於s(1,j)站的子問題就是通過s(1,j-1)和s(2,j-1)的最快路線,建立最優解的結構後,就可以執行動態規劃的第二步,用子問題的最優解遞迴定義乙個問題的最優解,根據前面的分析,很容易得到關於第j個站的最優解遞迴公式:

剩下的就是寫演算法實現問題了,下面就給出動態規劃法的演算法,首先定義乙個資料結構,這個資料結構是為了方便中間資料儲存和減少函式引數而定義的,並不是動態規劃方法的一部分:

#define max_line_station 20

typedef struct tagfixlinepara

fixlinepara;

int findfaststationsequence(fixlinepara *para, int n)

f[0][0] = para->a[0][0] + para->e[0];

f[1][0] = para->a[1][0] + para->e[1];

for(int j = 1; j < n; j++)

else

if((f[1][j - 1] + para->a[1][j]) <= (f[0][j - 1] + para->t[0][j] + para->a[1][j]))

else

}if(f[0][n - 1] + para->x[0] <= f[1][n - 1] + para->x[1])

else

return 0;

}求解完成後para->ls存放最有使用的裝配站所屬的裝配線編號,para->l存放底盤在兩條裝配線上的轉移記錄,結合para->ls可以向前遞推出底盤的裝配順序。para->fs儲存最短時間。下面是列印結果的函式:

void printstations(fixlinepara *para, int n)

}這個列印結果剛好和實際裝配順序是反的,因為它是通過para->ls向前遞推得出的結果。《演算法導論》提示可以通過遞迴的方法得出正序列的結果,但是是作為練習題提出的,沒有給出演算法,其實也不難,這裡就給出乙個:

void printnextstations(fixlinepara *para, int n, int line)

int i = para->l[line - 1][n];

printnextstations(para, n - 1, i);

printf("line %d , station %d/n", i, n);

}void printstations2(fixlinepara *para, int n)

《演算法導論》提到了可以通過窮舉(通常使用遞迴)方法解決這個問題,這裡也一併給出使用窮舉搜尋的方法,在裝配站個數n比較小的情況下還是可以可以用的。同樣,先定義乙個資料結構:

typedef struct tagfixlineenumeratepara

fixlineenumeratepara;

para->l存放搜尋過程中的乙個中間結果的裝配站序列,para->fs是中間結果的裝配時間,para->fl是最終的最優解的裝配站序列,para->ffs就是最優解的裝配時間。

int findenumeratestationsequence(fixlineenumeratepara *para, int line, int station, int n)

return 0;

}int curcost = para->fs + para->a[line][station - 1];

para->l[station - 1] = line + 1;

station++;

para->fs = curcost;

findenumeratestationsequence(para, line, station, n);

para->fs = curcost;

int nextline = (line + 1) % 2;

para->fs += para->t[line][station - 1];

findenumeratestationsequence(para, nextline, station, n);

return 0;

}int findfaststationsequenceenumerate(fixlineenumeratepara *para, int n)

搜尋是按照裝配站的順序遞迴的,所以結果就是正序列,列印結果的函式就很簡單了:

void printstations3(fixlineenumeratepara *para, int n)

}以上就是兩種方法的解,曬完收工。

裝配線(工作站)問題的兩種解法

上學的時候有一道題目一直困擾著我,那就是廚師擺盤子問題,問題的描述是這樣的 廚師的所有盤子都放在乙個架子上,每天工作結束他都要將盤子按照從小到大的順序排好,問題是架子不太穩,如果一次拿出乙個或幾個盤子,架子可能要倒掉,所以他必須只能從一邊翻動盤子,由於他只有兩隻手,所以只能用兩隻手將拿起的盤子一起翻...

裝配線(工作站)問題的兩種解法

上學的時候有一道題目一直困擾著我,那就是廚師擺盤子問題,問題的描述是這樣的 廚師的所有盤子都放在乙個架子上,每天工作結束他都要將盤子按照從小到大的順序排好,問題是架子不太穩,如果一次拿出乙個或幾個盤子,架子可能要倒掉,所以他必須只能從一邊翻動盤子,由於他只有兩隻手,所以只能用兩隻手將拿起的盤子一起翻...

裝配線(工作站)問題的兩種解法

每一條裝配線上有n個裝配站,編號為j 1,2,n,將裝配線i i為1或2 的第j個裝配站表示為s i,j 裝配線1的第j個站s 1,j 和裝配線2的第j個站s 2,j 執行相同的功能。然而這些裝配站是在不同的時間建造的,並且採用了不同的技術,因此,每個站上完成裝配所需要的時間也不相同,即使是在兩條裝...