日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

算法系列

 昵稱1740930 2012-03-30

算法系列---回溯算法

引言
      尋找問(wèn)題的解的一種可靠的方法是首先列出所有候選解,然后依次檢查每一個(gè),在檢查完所有或部分候選解后,即可找到所需要的解。理論上,當(dāng)候選解數(shù)量有限并且通過(guò)檢查所有或部分候選解能夠得到所需解時(shí),上述方法是可行的。不過(guò),在實(shí)際應(yīng)用中,很少使用這種方法,因?yàn)楹蜻x解的數(shù)量通常都非常大(比如指數(shù)級(jí),甚至是大數(shù)階乘),即便采用最快的計(jì)算機(jī)也只能解決規(guī)模很小的問(wèn)題。對(duì)候選解進(jìn)行系統(tǒng)檢查的方法有多種,其中回溯和分枝定界法是比較常用的兩種方法。按照這兩種方法對(duì)候選解進(jìn)行系統(tǒng)檢查通常會(huì)使問(wèn)題的求解時(shí)間大大減少(無(wú)論對(duì)于最壞情形還是對(duì)于一般情形)。事實(shí)上,這些方法可以使我們避免對(duì)很大的候選解集合進(jìn)行檢查,同時(shí)能夠保證算法運(yùn)行結(jié)束時(shí)可以找到所需要的解。因此,這些方法通常能夠用來(lái)求解規(guī)模很大的問(wèn)題。

算法思想
      回溯(backtracking)是一種系統(tǒng)地搜索問(wèn)題解答的方法。為了實(shí)現(xiàn)回溯,首先需要為問(wèn)題定義一個(gè)解空間(solution space),這個(gè)空間必須至少包含問(wèn)題的一個(gè)解(可能是最優(yōu)的)。

     下一步是組織解空間以便它能被容易地搜索。典型的組織方法是圖(迷宮問(wèn)題)或樹(shù)(N皇后問(wèn)題)。
      一旦定義了解空間的組織方法,這個(gè)空間即可按深度優(yōu)先的方法從開(kāi)始節(jié)點(diǎn)進(jìn)行搜索。

回溯方法的步驟如下:
     1) 定義一個(gè)解空間,它包含問(wèn)題的解。
     2) 用適于搜索的方式組織該空間。
     3) 用深度優(yōu)先法搜索該空間,利用限界函數(shù)避免移動(dòng)到不可能產(chǎn)生解的子空間。
回溯算法的一個(gè)有趣的特性是在搜索執(zhí)行的同時(shí)產(chǎn)生解空間。在搜索期間的任何時(shí)刻,僅保留從開(kāi)始節(jié)點(diǎn)到當(dāng)前節(jié)點(diǎn)的路徑。因此,回溯算法的空間需求為O(從開(kāi)始節(jié)點(diǎn)起最長(zhǎng)路徑的長(zhǎng)度)。這個(gè)特性非常重要,因?yàn)榻饪臻g的大小通常是最長(zhǎng)路徑長(zhǎng)度的指數(shù)或階乘。所以如果要存儲(chǔ)全部解空間的話,再多的空間也不夠用。

算法應(yīng)用
回溯算法的求解過(guò)程實(shí)質(zhì)上是一個(gè)先序遍歷一棵"狀態(tài)樹(shù)"的過(guò)程,只是這棵樹(shù)不是遍歷前預(yù)先建立的,而是隱含在遍歷過(guò)程中<<數(shù)據(jù)結(jié)構(gòu)>>(嚴(yán)蔚敏).
(1) 冪集問(wèn)題(組合問(wèn)題) (參見(jiàn)《數(shù)據(jù)結(jié)構(gòu)》(嚴(yán)蔚敏))
     求含N個(gè)元素的集合的冪集。
     如對(duì)于集合A={1,2,3},則A的冪集為
     p(A)={{1,2,3},{1,2},{1,3},{1},{2,3},{2},{3},Φ}
冪集的每個(gè)元素是一個(gè)集合,它或是空集,或含集合A中的一個(gè)元素,或含A中的兩個(gè)元素,或者等于集合A。反之,集合A中的每一個(gè)元素,它只有兩種狀態(tài):屬于冪集的元素集,或不屬于冪集元素集。則求冪集P(A)的元素的過(guò)程可看成是依次對(duì)集合A中元素進(jìn)行“取”或“舍”的過(guò)程,并且可以用一棵狀態(tài)樹(shù)來(lái)表示。求冪集元素的過(guò)程即為先序遍歷這棵狀態(tài)樹(shù)的過(guò)程。

下面給出程序:

#include <stdio.h>
#include 
<malloc.h>
#define ERROR 0
#define OK 1

typedef 
int ElemType;
typedef 
struct LNode
{
    ElemType data;
    
struct LNode *next;
}
 LNode,*LinkList;

//初始化
LinkList ListInit()
{

    LNode 
*base=(LinkList)malloc(sizeof(LNode));
    
base->data=0;
    
base->next=NULL;
    
return base;
}

//插入一個(gè)元素
int ListInsert(LinkList L,int i,ElemType e)
{
    LNode 
*p,*s;
    
int j=0;
    p
=(LNode *)L;
    
while(p&&j<i-1)
    
{
        p
=p->next;
        
++j;
    }

    
if(!p||j>i-1)
        
return ERROR;
    s
=(LNode *)malloc(sizeof(LNode));
    s
->data=e;
    s
->next=p->next;
    p
->next=s;
    
return OK;
}

//刪除一個(gè)結(jié)點(diǎn)
int ListDelete(LinkList &L,int i,ElemType &e)
{
    LinkList p
=L,q;
    
int j=0;
    
while(p->next&&j<i-1)
    
{
        p
=p->next;
        
++j;
    }

    
if(!(p->next)||j>i-1)
        
return ERROR;
    q
=p->next;
    p
->next=q->next;
    e
=q->data;
    free(q);
}

//長(zhǎng)度
int ListLength(LinkList L)
{
    LinkList p
=L;
    
int j=0;
    
if(!L)
        
return ERROR;
    
while(p->next)
    
{
        p
=p->next;
        
++j;
    }

    
return j;
}

//查找一個(gè)元素
int GetElem(LinkList L,int i,ElemType &e)
{
    LNode 
*p=L;
    
int j=0;
    
while(p->next&&j<i)
    
{
        p
=p->next;
        
++j;
    }

    
if(!p||j>i)
        
return ERROR;
    e
=p->data;
    
return OK;
}

//輸出鏈表元素
void Display(LinkList L)
{
    LNode 
*p=L;
    
if(!(p->next))
    
{
        printf(
"NULL,");
        
return;
    }

    
else
        p
=p->next;
    
while(p)
    
{

        printf(
"%d,",p->data);
        p
=p->next;    
    }

}

//求冪集
void PowerSet(int i,LinkList A,LinkList &B)
{
    
int k=0;
    ElemType e
=0;
    
if(i>ListLength(A))
    
{
        Display(B);
        printf(
"\n");
    }

    
else
    
{
        GetElem(A,i,e);
        k
=ListLength(B);
        ListInsert(B,k
+1,e);
        PowerSet(i
+1,A,B);

        ListDelete(B,k
+1,e);
        PowerSet(i
+1,A,B);
    }

}


int main()
{
    LinkList list
=ListInit(); //初始化
    LinkList list2=ListInit();//初始化

    ListInsert(list,
1,1);//插入元素
    ListInsert(list,2,2);
    ListInsert(list,
3,3);

    Display(list);
//輸出元素
    printf("\npower set is:\n");
    PowerSet(
1,list,list2);//求冪集
}

 

(2)迷宮問(wèn)題(參見(jiàn)《數(shù)據(jù)結(jié)構(gòu)》(嚴(yán)蔚敏))
計(jì)算機(jī)解迷宮時(shí),通常用的是"試探和回溯"的方法,即從入口出發(fā),順某一方向向前探索,若能走通,則繼續(xù)往前走;否則沿原路退回,換一個(gè)方向再繼續(xù)探索,直至所有可能的通路都探索到為止,如果所有可能的通路都試探過(guò),還是不能走到終點(diǎn),那就說(shuō)明該迷宮不存在從起點(diǎn)到終點(diǎn)的通道。

  1.從入口進(jìn)入迷宮之后,不管在迷宮的哪一個(gè)位置上,都是先往東走,如果走得通就繼續(xù)往東走,如果在某個(gè)位置上往東走不通的話,就依次試探往南、往西和往北方向,從一個(gè)走得通的方向繼續(xù)往前直到出口為止;

  2.如果在某個(gè)位置上四個(gè)方向都走不通的話,就退回到前一個(gè)位置,換一個(gè)方向再試,如果這個(gè)位置已經(jīng)沒(méi)有方向可試了就再退一步,如果所有已經(jīng)走過(guò)的位置的四個(gè)方向都試探過(guò)了,一直退到起始點(diǎn)都沒(méi)有走通,那就說(shuō)明這個(gè)迷宮根本不通;
  
   3.所謂"走不通"不單是指遇到"墻擋路",還有"已經(jīng)走過(guò)的路不能重復(fù)走第二次",它包括"曾經(jīng)走過(guò)而沒(méi)有走通的路"。顯然為了保證在任何位置上都能沿原路退回,需要用一個(gè)"后進(jìn)先出"的結(jié)構(gòu)即棧來(lái)保存從入口到當(dāng)前位置的路徑。并且在走出出口之后,棧中保存的正是一條從入口到出口的路徑。

由此,求迷宮中一條路徑的算法的基本思想是:
若當(dāng)前位置"可通",則納入"當(dāng)前路徑",并繼續(xù)朝"下一位置"探索;若當(dāng)前位置"不可通",則應(yīng)順著"來(lái)的方向"退回到"前一通道塊",然后朝著除"來(lái)向"之外的其他方向繼續(xù)探索;若該通道塊的四周四個(gè)方塊均"不可通",則應(yīng)從"當(dāng)前路徑"上刪除該通道塊。

設(shè)定當(dāng)前位置的初值為入口位置;
  do{
    若當(dāng)前位置可通,
    則{
     將當(dāng)前位置插入棧頂;       // 納入路徑
     若該位置是出口位置,則算法結(jié)束;
      // 此時(shí)棧中存放的是一條從入口位置到出口位置的路徑
     否則切換當(dāng)前位置的東鄰方塊為新的當(dāng)前位置;
     }
    否則
    {
    若棧不空且棧頂位置尚有其他方向未被探索,
    則設(shè)定新的當(dāng)前位置為: 沿順時(shí)針?lè)较蛐D(zhuǎn)找到的棧頂位置的下一相鄰塊;
    若棧不空但棧頂位置的四周均不可通,
    則{ 刪去棧頂位置;         // 從路徑中刪去該通道塊
      若棧不空,則重新測(cè)試新的棧頂位置,
      直至找到一個(gè)可通的相鄰塊或出棧至??眨?
     }
   }
} while (棧不空);

程序如下:

 

#include <stdio.h>

#define WALL   0  //
#define CORRIDOR 1 //通道
#define PATH  9 //為路徑上的一塊
#define TRIED 2 //

#define ROW_NUM    7 //迷宮數(shù)組行數(shù)
#define COL_NUM   13 //列數(shù)

#define TRUE 1
#define FALSE 0
#define MAXSIZE 50
typedef 
struct 
{
    
int row;
    
int col;
}
PosType;

typedef 
struct 
{
    
int ord;      //通道塊在路徑上的"序號(hào)"
    PosType seat; //通道塊在迷宮中的坐標(biāo)
    int di;       //當(dāng)前通道塊的方向
}
SElemType;
typedef 
struct 
{
    SElemType S[MAXSIZE];
    
int top;
}
MazeType;
//迷宮
int grid[ROW_NUM][COL_NUM]={{1110111001111},
                            
{1011101111101},
                            
{1000001010101},
                            
{1000111010111},
                            
{1111100001000},
                            
{0000100000000},
                            
{0000111111111}}
;
//當(dāng)前位置是否可以通過(guò)
bool Valid(PosType pos)
{
    
if(pos.row>=0&&pos.row<=ROW_NUM&&pos.col>=0&&pos.col<=COL_NUM&&grid[pos.row][pos.col]==CORRIDOR)
        
return TRUE;
    
else
        
return FALSE;
}

void FootPrint(PosType pos)//留下足跡
{
    grid[pos.row][pos.col]
=PATH;
}

void Undo(PosType pos) //留下不能通過(guò)的標(biāo)識(shí)
{
    grid[pos.row][pos.col]
=TRIED;
}

//當(dāng)前位置的下一個(gè)位置
PosType NextPos(PosType cur,int di)
{
    PosType next;
    
switch(di)
    
{
    
case 0//
        next.row=cur.row;
        next.col
=cur.col+1;
        
break;
    
case 1//
        next.row=cur.row+1;
        next.col
=cur.col;
        
break;
    
case 2:  //西
        next.row=cur.row;
        next.col
=cur.col-1;
        
break;
    
case 3:  //
        next.row=cur.row-1;
        next.col
=cur.col;
        
break;
    }

    
return next;
}

//是否到達(dá)終點(diǎn)
bool Done(PosType cur,PosType end)
{
    
if(cur.row==end.row&&cur.col==end.col)
        
return TRUE;
    
else
        
return FALSE;
}

//尋找迷宮路徑
bool MazePath(MazeType &path,PosType start,PosType end)
{
    SElemType e;
    path.top
=-1;
    
int step=1;
    PosType curpos
=start;
    
do
    
{
        
if(Valid(curpos))
        
{
            FootPrint(curpos);
            e.ord
=step;
            e.di
=0;
            e.seat
=curpos;
            path.S[
++path.top]=e;
            
if(Done(curpos,end))
                
return TRUE;
            curpos
=NextPos(curpos,0);
            step
++;
        }

        
else
        
{
            
if(path.top>-1)//棧不空
            {
                e
=path.S[path.top--];
                
while(e.di==3&&path.top>-1)
                
{
                    Undo(e.seat);
                    e
=path.S[path.top--];
                }

                
if(e.di<3)
                
{
                    e.di
++;
                    path.S[
++path.top]=e;
                    curpos
=NextPos(e.seat,e.di);
                }

            }
//if
        }
//else
    }
while(path.top>-1);
    
return FALSE;
}

//輸出路徑
void PrintPath(MazeType path)
{
    
int i=0;
    
while(i<=path.top)
    
{
        printf(
"第%d步:(%d,%d)\n",path.S[i].ord,path.S[i].seat.row,path.S[i].seat.col);
        i
++;
    }

}

//輸出路徑
void PrintPath2()
{
    
for(int i=0;i<ROW_NUM;i++)
        
for(int j=0;j<COL_NUM;j++)
        
if(grid[i][j]==PATH)
            printf(
"(%d,%d)\n",i,j);
}

int main()
{
    MazeType path;
    PosType start
={0,0},end={6,12};
    
if(MazePath(path,start,end))
        PrintPath(path);
    
else
        printf(
"not reachable!\n");

    PrintPath2();
}

(3)N皇后問(wèn)題:
在一個(gè)N*N的棋盤(pán)上放置N個(gè)皇后,且使得每?jī)蓚€(gè)之間不能互相攻擊,也就是使得每?jī)蓚€(gè)不在同一行,同一列和同一斜角線上。
對(duì)于N=1,問(wèn)題的解很簡(jiǎn)單,而且我們很容易看出對(duì)于N=2和N=3來(lái)說(shuō),這個(gè)問(wèn)題是無(wú)解的。所讓我們考慮4皇后問(wèn)題并用回溯法對(duì)它求解。因?yàn)槊總€(gè)皇后都必須分別占據(jù)—行,我們需要做的不過(guò)是為圖1棋盤(pán)上的每個(gè)皇后分配一列。

 
     我們從空棋盤(pán)開(kāi)始,然后把皇后1放到它所在行的第一個(gè)可能位置上,也就是第一行第—列。對(duì)于皇后2,在經(jīng)過(guò)第一列和第二列的失敗嘗試之后,我們把它放在第一個(gè)可能的位置,就是格子〔2,3),位于第二行第二列的格子。這被證明是一個(gè)死胡同,因?yàn)榛屎螅簩](méi)有位置可放。所以,該算法進(jìn)行回溯,把皇后2放在下一個(gè)可能位置(2,4)上。然后皇后3就可以放在(3,2),這被證明是另一個(gè)死胡同。該算法然后就回溯到底,把皇后1移到(1,2)。接著皇后2(2,4),皇后3(3,1),而皇后4(4,3),這就是該問(wèn)題的一個(gè)解。圖2給出了這個(gè)查找的狀態(tài)空間樹(shù)。

 程序如下:

 

#include <stdio.h>
#include 
<math.h>
#define N  4
int col[N+1];
//輸出結(jié)果
void Output()
{
    
for(int i=1;i<=N;i++)
    
{
        printf(
"(%d,%d)\n",i,col[i]);
    }

    printf(
"\n");
}

//求解函數(shù)
void Queen(int i,int n)
{
    
if(i>n)
        Output();
    
else
    
{
        
for(int j=1;j<=n;++j)
        
{
            
int k=1;
            col[i]
=j;
            
while(k<i)
            
{
                
if((col[k]-col[i])*(fabs(col[k]-col[i])-fabs(k-i))!=0)
                
{
                    k
++;
                    
if(k==i)
                        Queen(i
+1,n);
                }

                
else
                
{
                    
break;
                }

            }

        }

    }

}

int main()
{
    printf(
"the answer is:\n");
    
for(int i=1;i<=N;i++)
    
{
        col[
1]=i; //設(shè)置第一行
        Queen(2,N);
    }

}

結(jié)果如下:

 

標(biāo)簽: 算法

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多