貪婪法

貪婪法是一種不追求最優解,只希望得到較爲滿意解的方法。貪婪法一般可以快速得到滿意的解,因爲它省去了爲找最優解要窮盡所有可能而必須耗費的大量時間。貪婪法常以當前情況爲基礎作最優選擇,而不考慮各種可能的整體情況,所以貪婪法不要回溯。

       例如平時購物找錢時,爲使找回的零錢的硬幣數最少,不考慮找零錢的所有各種發表方案,而是從最大面值的幣種開始,按遞減的順序考慮各幣種,先儘量用大面值的幣種,當不足大面值幣種的金額時纔去考慮下一種較小面值的幣種。這就是在使用貪婪法。這種方法在這裏總是最優,是因爲銀行對其發行的硬幣種類和硬幣面值的巧妙安排。如只有面值分別爲1511單位的硬幣,而希望找回總額爲15單位的硬幣。按貪婪算法,應找111單位面值的硬幣和41單位面值的硬幣,共找回5個硬幣。但最優的解應是35單位面值的硬幣。

【問題】       裝箱問題

問題描述:裝箱問題可簡述如下:設有編號爲01、…、n-1n種物品,體積分別爲v0v1、…、vn-1。將這n種物品裝到容量都爲V的若干箱子裏。約定這n種物品的體積均不超過V,即對於0in,有0viV。不同的裝箱方案所需要的箱子數目可能不同。裝箱問題要求使裝盡這n種物品的箱子數要少。

       若考察將n種物品的集合分劃成n個或小於n個物品的所有子集,最優解就可以找到。但所有可能劃分的總數太大。對適當大的n,找出所有可能的劃分要花費的時間是無法承受的。爲此,對裝箱問題採用非常簡單的近似算法,即貪婪法。該算法依次將物品放到它第一個能放進去的箱子中,該算法雖不能保證找到最優解,但還是能找到非常好的解。不失一般性,設n件物品的體積是按從大到小排好序的,即有v0v1≥…≥vn-1。如不滿足上述要求,只要先對這n件物品按它們的體積從大到小排序,然後按排序結果對物品重新編號即可。裝箱算法簡單描述如下:

{     輸入箱子的容積;

       輸入物品種數n

       按體積從大到小順序,輸入各物品的體積;

       預置已用箱子鏈爲空;

       預置已用箱子計數器box_count0

       for (i=0;i<n;i++)

       {     從已用的第一隻箱子開始順序尋找能放入物品i 的箱子j

              if (已用箱子都不能再放物品i

              {     另用一個箱子,並將物品i放入該箱子;

                     box_count++

              }

              else

                     將物品i放入箱子j

       }

}

       上述算法能求出需要的箱子數box_count,並能求出各箱子所裝物品。下面的例子說明該算法不一定能找到最優解,設有6種物品,它們的體積分別爲:604535202020單位體積,箱子的容積爲100個單位體積。按上述算法計算,需三隻箱子,各箱子所裝物品分別爲:第一隻箱子裝物品13;第二隻箱子裝物品245;第三隻箱子裝物品6。而最優解爲兩隻箱子,分別裝物品145236

       若每隻箱子所裝物品用鏈表來表示,鏈表首結點指針存於一個結構中,結構記錄尚剩餘的空間量和該箱子所裝物品鏈表的首指針。另將全部箱子的信息也構成鏈表。以下是按以上算法編寫的程序。

【程序】

# include <stdio.h>

# include <stdlib.h>

typedef  struct  ele

{     int  vno;

       struct  ele  *link;

}     ELE;

typedef  struct  hnode

{     int  remainder;

       ELE  *head;

       Struct  hnode  *next;

}     HNODE;

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

void  main()

{     int  n, i, box_count, box_volume, *a;

       HNODE  *box_h,  *box_t,  *j;

       ELE   *p,  *q;

       Printf(“輸入箱子容積/n”);

       Scanf(“%d”,&box_volume);

       Printf(“輸入物品種數/n”);

       Scanf(“%d”,&n);

       A=(int *)malloc(sizeof(int)*n);

       Printf(“請按體積從大到小順序輸入各物品的體積:”);

       For (i=0;i<n;i++)    scanf(“%d”,a+i);

       Box_h=box_t=NULL;

       Box_count=0;

       For (i=0;i<n;i++)

       {     p=(ELE *)malloc(sizeof(ELE));

              p->vno=i;

              for (j=box_h;j!=NULL;j=j->next)

                     if (j->remainder>=a[i])   break;

              if (j==NULL)

              {     j=(HNODE *)malloc(sizeof(HNODE));

                     j->remainder=box_volume-a[i];

                     j->head=NULL;

                     if (box_h==NULL)        box_h=box_t=j;

                     else  box_t=boix_t->next=j;

                     j->next=NULL;

                     box_count++;

              }

              else  j->remainder-=a[i];

              for (q=j->next;q!=NULL&&q->link!=NULL;q=q->link);

              if (q==NULL)

              {     p->link=j->head;

                     j->head=p;

              }

              else

              {     p->link=NULL;

                     q->link=p;

              }

       }

       printf(“共使用了%d只箱子box_count);

       printf(“各箱子裝物品情況如下:”);

       for (j=box_h,i=1;j!=NULL;j=j->next,i++)

       {     printf(“%2d只箱子,還剩餘容積%4d,所裝物品有;/n”,I,j->remainder);

              for (p=j->head;p!=NULL;p=p->link)

                     printf(“%4d”,p->vno+1);

              printf(“/n”);

       }

}

【問題】       馬的遍歷

問題描述:在8×8方格的棋盤上,從任意指定的方格出發,爲馬尋找一條走遍棋盤每一格並且只經過一次的一條路徑。

       馬在某個方格,可以在一步內到達的不同位置最多有8個,如圖所示。如用二維數組board[ ][ ]表示棋盤,其元素記錄馬經過該位置時的步驟號。另對馬的8種可能走法(稱爲着法)設定一個順序,如當前位置在棋盤的(ij)方格,下一個可能的位置依次爲(i+2j+1)、(i+1j+2)、(i-1j+2)、(i-2j+1)、(i-2j-1)、(i-1j-2)、(i+1j-2)、(i+2j-1),實際可以走的位置盡限於還未走過的和不越出邊界的那些位置。爲便於程序的同意處理,可以引入兩個數組,分別存儲各種可能走法對當前位置的縱橫增量。

 

4

 

3

 

5

 

 

 

2

 

 

 

 

6

 

 

 

1

 

7

 

0

 

 

       對於本題,一般可以採用回溯法,這裏採用Warnsdoff策略求解,這也是一種貪婪法,其選擇下一出口的貪婪標準是在那些允許走的位置中,選擇出口最少的那個位置。如馬的當前位置(ij)只有三個出口,他們是位置(i+2j+1)、(i-2j+1)和(i-1j-2),如分別走到這些位置,這三個位置又分別會有不同的出口,假定這三個位置的出口個數分別爲423,則程序就選擇讓馬走向(i-2j+1)位置。

       由於程序採用的是一種貪婪法,整個找解過程是一直向前,沒有回溯,所以能非常快地找到解。但是,對於某些開始位置,實際上有解,而該算法不能找到解。對於找不到解的情況,程序只要改變8種可能出口的選擇順序,就能找到解。改變出口選擇順序,就是改變有相同出口時的選擇標準。以下程序考慮到這種情況,引入變量start,用於控制8種可能着法的選擇順序。開始時爲0,當不能找到解時,就讓start1,重新找解。細節以下程序。

【程序】

# include <stdio.h>

int delta_i[ ]={2,1,-1,-2,-2,-1,1,2};

int delta_j[ ]={1,2,2,1,-1,-2,-2,-1};

int board[8][8];

int exitn(int i,int j,int s,int a[ ])

{     int i1,j1,k,count;

       for (count=k=0;k<8;k++)

       {     i1=i+delta_i[(s+k)%8];

              j1=i+delta_j[(s+k)%8];

              if (i1>=0&&i1<8&&j1>=0&&j1<8&&board[I1][j1]==0)

                     a[count++]=(s+k)%8;

       }

       return count;

}

 

int next(int i,int j,int s)

{     int m,k,mm,min,a[8],b[8],temp;

       m=exitn(i,j,s,a);

       if (m==0)              return –1;

       for (min=9,k=0;k<m;k++)

       {     temp=exitn(I+delta_i[a[k]],j+delta_j[a[k]],s,b);

              if (temp<min)

              {     min=temp;

kk=a[k];

              }

       }

       return  kk;

}

 

void main()

{     int sx,sy,i,j,step,no,start;

       for (sx=0;sx<8;sx++)

       for (sy=0;sy<8;sy++)

       {     start=0;

              do {

                     for (i=0;i<8;i++)

                            for (j=0;j<8;j++)

                                   board[i][j]=0;

                     board[sx][sy]=1;

                     I=sx;       j=sy;

                     For (step=2;step<64;step++)

                     {     if ((no=next(i,j,start))==-1)   break;

                            I+=delta_i[no];

                            j+=delta_j[no];

                            board[i][j]=step;

                     }

                     if (step>64)    break;

                     start++;

              } while(step<=64)

              for (i=0;i<8;i++)

              {     for (j=0;j<8;j++)

                            printf(“%4d”,board[i][j]);

                     printf(“/n/n”);

              }

              scanf(“%*c”);

       }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章