分支限界法类似于回溯法,是一种在问题的解空间树上搜索问题解的算法。 分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。 分支限界法常以广度优先的方式搜索问题的解空间树。 在分支限界法中,每一个活结点只有一次机会成为扩展结点。 活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。 此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。 从活结点表中选择下一扩展结点的不同方式导致不同的分支限界法: 队列式(FIFO)分支限界法:按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。 优先队列式分支限界法:按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。 最大优先队列:使用最大堆,体现最大效益优先 最小优先队列:使用最小堆,体现最小费用优先 【例】一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如: 阵列 4 10 0234500067 1034560500 2045600671 0000000089 有4个细胞。 #include using namespace std; int dx[4]={-1,0,1,0}, // x,y 方向上的增量 dy[4]={0,1,0,-1}; int bz[100][100],num=0,n,m; //二维数组,存储原始矩阵 void doit(int p,int q){ //p,q矩阵的行列号 int x,y,t,w,i; int h[1000][2]; //顺序队列,记录入队细胞元素在二维数组中的位置 num++; //细胞个数增1 bz[p][q]=0; //细胞元素清0 t=0;w=1; //队列指针。t队首,w 队尾 h[1][1]=p; h[1][2]=q; //遇到的第一个细胞入队 do { t++; //队头指针加1 for (i=0;i<=3;i++){ //沿细胞的上下左右四个方向搜索细胞 x=h[t][1]+dx[i]; y=h[t][2]+dy[i]; if ((x>=0)&&(x<m)&&(y>=0)&&(y<n)&&(bz[x][y])){ w++; h[w][1]=x; h[w][2]=y; bz[x][y]=0; } //本方向搜索到细胞就入队 } }while (t<w); //直至队空为止 } int main(){ int i,j; char s[100],ch; scanf("%d%d\n",&m,&n); for (i=0; i<=m-1;i++ ) for (j=0;j<=n-1;j++ ) bz[i][j]=1; //初始化 for (i=0;i<=m-1;i++) { gets(s); for (j=0;j<=n-1;j++) if (s[j]==‘0’) bz[i][j]=0; } for (i=0;i<=m-1;i++) for (j=0;j<=n-1;j++) if (bz[i][j]) doit(i,j); //在矩阵中寻找细胞 printf(“NUMBER of cells=%d”,num); return 0; }
例二最少步数 在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”。有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字。他的同桌平时喜欢下围棋,知道这件事后觉得很有趣,就想试一试,在一个(100*100)的围棋盘上任选两点A、B,A点放上黑子,B点放上白子,代表两匹马。棋子可以按“日”字走,也可以按“田”字走,俩人一个走黑马,一个走白马。谁用最少的步数走到左上角坐标为(1,1)的点时,谁获胜。现在他请你帮忙,给你A、B两点的坐标,想知道两个位置到(1,1)点可能的最少步数。 #include #include #include using namespace std; int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2}, dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1}; int main(){ int s[101][101],que[10000][4]={0},x1,y1,x2,y2; memset(s,0xff,sizeof(s)); //s数组的初始化 int head=1,tail=1; //初始位置入队 que[1][1]=1;que[1][2]=1;que[1][3]=0; cin>>x1>>y1>>x2>>y2; //读入黑马和白马的出发位置 while(head<=tail) { //若队列非空,则扩展队首结点 for(int d=0;d<=11;d++){ //枚举12个扩展方向 int x=que[head][1]+dx[d]; //计算马按d方向跳跃后的位置 int y=que[head][2]+dy[d]; if(x>0&&y>0&&x<=100&&y<=100) if(s[x][y]==-1) { //若(x,y)满足约束条件 s[x][y]=que[head][3]+1; //计算(1,1)到(x,y)的最少步数 tail++; //(1,1)至(x,y)的最少步数入队 que[tail][1]=x; que[tail][2]=y; que[tail][3]=s[x][y]; if(s[x1][y1]>0&&s[x2][y2]>0){ //输出问题的解 cout<<s[x1][y1]<<endl; cout<<s[x2][y2]<<endl; system(“pause”); return 0; } } } head++; } }
例题三迷宫问题 如下图所示,给出一个N*M的迷宫图和一个入口、一个出口。 1 2 编一个程序,打印一条从迷宫入口到出口的路径。这里黑色方块的单元表示走不通(用-1表示),白色方块的单元表示可以走(用0表示)。只能往上、下、左、右四个方向走。如果无路则输出“no way.”。
#include
using namespace std; int n,m,desx,desy,soux,souy,totstep,a[51],b[51],map[51][51]; bool f; int move(int x, int y,int step){ map[x][y]=step; //走一步,作标记,把步数记下来 a[step]=x; b[step]=y; //记路径 if ((xdesx)&&(ydesy)) { f=1; totstep=step; } else { if ((y!=m)&&(map[x][y+1]==0)) move(x,y+1,step+1); //向右 if ((!f)&&(x!=n)&&(map[x+1][y]==0)) move(x+1,y,step+1); //往下 if ((!f)&&(y!=1)&&(map[x][y-1]==0)) move(x,y-1,step+1); //往左 if ((!f)&&(x!=1)&&(map[x-1][y]0)) move(x-1,y,step+1); //往上 } } int main(){ int i,j; cin>>n>>m; //n行m列的迷宫 for (i=1;i<=n;i++) //读入迷宫,0表示通,-1表示不通 for (j=1;j<=m;j++) cin>>map[i][j]; cout<<“input the enter:”; cin>>soux>>souy; //入口 cout<<“input the exit:”; cin>>desx>>desy; //出口 f=0; //f=0表示无解;f=1表示找到了一个解 move(soux,souy,1); if (f) { for (i=1;i<=totstep;i++) //输出直迷宫的路径 cout<<a[i]<<","<<b[i]<<endl; } else cout<<“no way.”<<endl; return 0; } #include using namespace std; int u[5]={0,0,1,0,-1}, w[5]={0,1,0,-1,0}; int n,m,i,j,desx,desy,soux,souy,head,tail,x,y,a[51],b[51],pre[51],map[51][51]; bool f; int print(int d){ if (pre[d]!=0) print (pre[d]); //递归输出路径 cout<<a[d]<<","<<b[d]<<endl; } int main(){ int i,j; cin>>n>>m; //n行m列的迷宫 for (i=1;i<=n;i++) //读入迷宫,0表示通,-1表示不通 for (j=1;j<=m;j++) cin>>map[i][j]; cout<<“input the enter:”; cin>>soux>>souy; //入口 cout<<“input the exit:”; cin>>desx>>desy; //出口 head=0; tail=1; f=0; map[soux][souy]=-1; a[tail]=soux; b[tail]=souy; pre[tail]=0; while (head!=tail) { //队列不为空 head++; for (i=1;i<=4;i++) { //4个方向 x=a[head]+u[i]; y=b[head]+w[i]; if ((x>0)&&(x<=n)&&(y>0)&&(y<=m)&&(map[x][y]0)) { //本方向上可以走 tail++; a[tail]=x; b[tail]=y; pre[tail]=head; map[x][y]=-1; if ((xdesx)&&(ydesy)) { //扩展出的结点为目标结点 f=1; print(tail); break; } } } if (f) break; } if (!f) cout<<“no way.”<<endl; return 0; }