常用演算法設計方法之窮舉搜尋法
窮舉搜尋法是對可能是解的眾多候選解按某種順序進行逐一列舉和檢驗,並從眾找出那些符合要求的候選解作為問題的解。
【問題】將A、B、C、D、E、F這六個變數排成如圖所示的三角形,這六個變數分別取[1,6]上的整數,且均不相同。求使三角形三條邊上的變數之和相等的全部解。如圖就是一個解。
程式引入變數a、b、c、d、e、f,並讓它們分別順序取1至6的證書,在它們互不相同的條件下,測試由它們排成的如圖所示的三角形三條邊上的變數之和是否相等,如相等即為一種滿足要求的排列,把它們輸出。當這些變數取盡所有的組合後,程式就可得到全部可能的解。細節見下面的程式。
【程式1】
# include <stdio.h>
void main()
{int a,b,c,d,e,f;
for (a=1;a<=6;a++)
for (b=1;b<=6;b++){
if (b==a)continue;
for (c=1;c<=6;c++){
if (c==a)||(c==b)continue;
for (d=1;d<=6;d++){
if (d==a)||(d==b)||(d==c)continue;
for (e=1;e<=6;e++){
if (e==a)||(e==b)||(e==c)||(e==d)continue;
f=21-(a+b+c+d+e);
if ((a+b+c==c+d+e))&&(a+b+c==e+f+a)){
printf(“%6d,a);
printf(“%4d%4d”,b,f);
printf(“%2d%4d%4d”,c,d,e);
scanf(“%*c”);
}
}
}
}
}
}
按窮舉法編寫的程式通常不能適應變化的情況。如問題改成有9個變數排成三角形,每條邊有4個變數的情況,程式的迴圈重數就要相應改變。
對一組數窮盡所有排列,還有更直接的方法。將一個排列看作一個長整數,則所有排列對應著一組整數。將這組整數按從小到大的順序排列排成一個整數,從對應最小的整數開始。按數列的遞增順序逐一列舉每個排列對應的每個整數,這能更有效地完成排列的窮舉。從一個排列找出對應數列的下一個排列可在當前排列的基礎上作部分調整來實現。倘若當前排列為
【程式2】
# include <stdio.h>
# define SIDE_N3
# define LENGTH3
# define VARIABLES6
int A,B,C,D,E,F;
int *pt[]={&A,&B,&C,&D,&E,&F};
int *side[SIDE_N][LENGTH]={&A,&B,&C,&C,&D,&E,&E,&F,&A};
int side_total[SIDE_N];
main{}
{int i,j,t,equal;
for (j=0;j<VARIABLES;j++)
*pt[j]=j+1;
while(1)
{for (i=0;i<SIDE_N;i++)
{for (t=j=0;j<LENGTH;j++)
t+=*side[j];
side_total=t;
}
for (equal=1,i=0;equal&&i<SIDE_N-1;i++)
if (side_total!=side_total[i+1]equal=0;
if (equal)
{for (i=1;i<VARIABLES;i++)
printf(“%4d”,*pt);
printf(“/n”);
scanf(“%*c”);
}
for (j=VARIABLES-1;j>0;j--)
if (*pt[j]>*pt[j-1])break;
if (j==0)break;
for (i=VARIABLES-1;i>=j;i--)
if (*pt>*pt[i-1])break;
t=*pt[j-1];* pt[j-1] =* pt; *pt=t;
for (i=VARIABLES-1;i>j;i--,j++)
{t=*pt[j]; *pt[j] =* pt; *pt=t;}
}
}
從上述問題解決的方法中,最重要的因素就是確定某種方法來確定所有的候選解。下面再用一個示例來加以說明。
【問題】揹包問題
問題描述:有不同價值、不同重量的物品n件,求從這n件物品中選取一部分物品的選擇方案,使選中物品的總重量不超過指定的限制重量,但選中物品的價值之和最大。
設n個物品的重量和價值分別儲存於陣列w[ ]和v[ ]中,限制重量為tw。考慮一個n元組(x0,x1,…,xn-1),其中xi=0 表示第i個物品沒有選取,而xi=1則表示第i個物品被選取。顯然這個n元組等價於一個選擇方案。用列舉法解決揹包問題,需要列舉所有的選取方案,而根據上述方法,我們只要列舉所有的n元組,就可以得到問題的解。
顯然,每個分量取值為0或1的n元組的個數共為2n個。而每個n元組其實對應了一個長度為n的二進位制數,且這些二進位制數的取值範圍為0~2n-1。因此,如果把0~2n-1分別轉化為相應的二進位制數,則可以得到我們所需要的2n個n元組。
【演算法】
maxv=0;
for (i=0;i<2n;i++)
{B[0..n-1]=0;
把i轉化為二進位制數,儲存於陣列B中;
temp_w=0;
temp_v=0;
for (j=0;j<n;j++)
{if (B[j]==1)
{temp_w=temp_w+w[j];
temp_v=temp_v+v[j];
}
if ((temp_w<=tw)&&(temp_v>maxv))
{maxv=temp_v;
儲存該B陣列;
}
}
}