Data_Structure/Vorlesungen/DS/Beispiele/min-packing.c
2025-01-20 21:25:33 +08:00

100 lines
4.2 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Program: min-packing.c (Report comments/bugs to chikh@yuntech.edu.tw)
Function: 將n個糖磚包裝成一大包每個糖磚長寬高都是10公分計算最少面積的包裝紙可打包好。
運作理念先對n決定可能的分割方式每一種分割即決定一種包裝方式。
n = 4 = 1+3 = 2+2 = 1+1+2 = 1+1+1+1代表可把糖磚包裝為
a) 僅1排4個擺成一排
b) 第1排擺1個、第2排擺3個
c) 第1排擺2個、第2排擺2個
d) 第1排擺1個、第2排擺1個、第3排擺2個
e) 第1排擺1個、第2排擺1個、第3排擺1個、第4排擺1個
注意上述a)與e)實為等義情況。
因糖磚總數固定,覆蓋上下面的包裝紙面積相同,不同的包裝方式造成周圍長度有差異。
若能計算每一種分割對應的擺置方式之週長,則可知哪種分割可得最佳組態,問題即得解。
Notes: 1) 透過整數分割另自訂shape資料結構以解決問題所需
2) 底下程式碼運作皆以磚個數為計量單數,最後才換算為真正的長寬高(公分)
3) 融入「動態規劃」(dynamic programming)技巧以dimension[]紀錄目前所知最佳組態
*/
#include <stdio.h>
typedef struct { /* 自訂shape資料結構描述將被包裝的幾何形狀物 */
int n; /* 所含磚數 */
int base; /* 該形狀的最大底邊長度 */
int faces; /* 形狀內糖磚共享接觸面數 */
int circumference; /* 形狀物最短週長 */
} shape;
int indx = 0; /* 指示堆疊頂端元素位置 */
int element[100] = {0}; /* 以陣列實作堆疊結構 */
/* 底下dimension[i]紀錄糖磚數為i的條件下目前所知之最佳組態(週長最短者) */
shape dimension[100] = {{0,0,0,0},{1,1,0,4},{2,2,1,6},{3,3,2,8}}; /* 初始化幾何形狀物的最佳尺寸 */
shape bestDimension(int n, shape s) /* 把dimension[n]與傳入的s形狀併接回傳合併後的組態 */
{
shape result; /* 合併後的結果,將作為函數回傳的內容 */
int minBase;
if (s.n == 0) /* s是空的 */
result = dimension[n];
else { /* 把dimension[n]與傳入的s併接決定新的最佳組態 */
minBase = dimension[n].base <= s.base? dimension[n].base : s.base; /* 計算二個別形狀之底邊最小者,將影響接觸面大小與週長的改變 */
result.n = n+s.n; /* 合併後的新形狀內含的糖磚數 */
result.faces = dimension[n].faces+s.faces + minBase; /* 共享接觸面數將增加 */
result.base = dimension[n].base >= s.base? dimension[n].base : s.base; /* 二個別形狀之底邊最大者為合併後的底邊長度 */
result.circumference = dimension[n].circumference + s.circumference - 2*minBase; /* 換算合併後的新形狀物之週長 */
//if ((dimension[result.n].n == 0) || (dimension[result.n].circumference > result.circumference))
if ((dimension[result.n].n == 0) || (dimension[result.n].circumference > result.circumference)
|| (dimension[result.n].circumference == result.circumference && dimension[result.n].base < result.base)) /* 若新組態優於dimension[]所記錄內容,則更新 */
dimension[result.n] = result;
}
//printf("磚數 = %d; 底邊 = %d; 共面數 = %d; 週長 = %d\n",result.n, result.base, result.faces, result.circumference);
return result;
}
shape partition(int n, int max, int addend) /* n:待分割的數、max:最大元素、addend:所採用的分解元素 */
{
int i;
shape result = {0,0,0,0}; /* 空的幾何形狀 */
static shape bestOne = {0,0,0,1000000}; /* 即將回傳的最佳組態,先預設週長為某超大的數值,隨後將更新 */
element[indx++] = addend; /* 相當於push()動作 */
if (n == 0) { /* 已觸底、無法再分割,把推疊內的內容取出 */
for (i = indx-1; i > 0; i--) /* 逐一合併所有可能分割所對應的幾何形狀物結果紀錄於result */
result = bestDimension(element[i],result);
//printf("磚數 = %d; 底邊 = %d; 共面數 = %d; 週長 = %d\n",result.n, result.base, result.faces, result.circumference);
if (bestOne.circumference > result.circumference) bestOne = result;
}
else /* n > 0意味著尚可繼續分割 */
for (i = 1; i <= max && i <= n; i++)
partition(n-i,i,i);
indx--; /* 等同pop()動作 */
return bestOne; /* 回傳最佳形狀 */
}
int main()
{
int n, i, numCube;
shape bestLayout;
printf("\n*** 最小包裝面積之郵寄包裹程式 ***\n\n輸入糖磚數 ==> ");
scanf("%d",&n);
//printf("\n最少包裝紙面積 = %d\n",partition(n,n,0).circumference*10*10+2*n*10*10);
bestLayout = partition(n,n,0);
printf("\n最少包裝紙面積 = %d\n",bestLayout.circumference*10*10+2*n*10*10);
printf("\n包裝方式為擺\放若干排:\n");
for (numCube = n, i = 1; numCube > 0; numCube -= bestLayout.base, i++) {
bestLayout = dimension[numCube];
printf("第%d排\%d個磚\n",i,bestLayout.base); /* 輸出留意有"許功蓋"問題,因此多加\以避免困擾 */
}
return 0;
}