回溯与分支限界算法设计.docx
《回溯与分支限界算法设计.docx》由会员分享,可在线阅读,更多相关《回溯与分支限界算法设计.docx(16页珍藏版)》请在冰豆网上搜索。
回溯与分支限界算法设计
算法设计与分析实验报告
专业
班级
姓名
学号
实验名称
实验四:
回溯与分支限界算法设计
实验目的
1.掌握回溯法解决问题的一般步骤。
2.学会使用回溯法解决实际问题。
3.掌握分支限界法解决问题的基本思想。
4.学会使用分支限界法解决实际问题。
实验内容
1.骑士游历问题(采用回溯法):
在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次。
若给定起始位置(x0,y0),编程探索出一条路径,沿着这条路径马能遍历棋盘上的所有单元格。
2.行列变换问题(采用分支限界法):
给定两个mn方格阵列组成的图形A和图形B,每个方格的颜色为黑色或白色,如下图所示。
行列变换问题的每一步变换可以交换任意2行或2列方格的颜色,或者将某行或某列颠倒。
上述每次变换算作一步。
试设计一个算法,计算最少需要多少步,才能将图形A变换为图形B。
算法描述
1.骑士游历问题的解题思路或算法思想:
如果在每步选择方向时,不是任意选择一个方向,而是经过一定的测试和计算,“预见”每条路的“宽窄”,再选择一条最“窄”的路先走,成功的可能性较大。
理由是先走“困难的路”,光明大道留在后面。
因为每一格迟早都要走到,与其把困难留在后面,不如先走“困难的路”,这样路就会越走越宽,成功的机会就越大。
这种方法称为预见算法。
为每个方向测定一个值――可通路数,它表示该位置上还有多少条通路。
在每一格上对8个方向都进行试探,并分析比较,下一步应该选择可通路数值最小的方向走。
2.行列变换问题的解题思路或算法思想:
先进先出队列式分支限界法
输入数据,将计算出的最少变换次数和相应的变换序列输出。
第1行是最少变换次数。
从第2行开始,每行用4个数表示一次变换。
程序及运行结果
1.骑士游历问题的程序:
packagecom.t5;
importjava.util.Scanner;
publicclassQishi{
privatebooleanTravel(intfirstX,intfirstY,int[][]board){
//对应骑士可走的8个方向
int[]movex={-2,-1,1,2,2,1,-1,-2};
int[]movey={1,2,2,1,-1,-2,-2,-1};
//下一步出路的位置
int[]nextStepX=newint[board.length];
int[]nextStepY=newint[board.length];
//记录出路的个数
int[]exitS=newint[board.length];
intnextX=firstX;
intnextY=firstY;
board[nextX][nextY]=1;
for(intm=2;m<=Math.pow(board.length,2);m++){
//初始化下一个位置可走的位置的数目
for(inti=0;iexitS[i]=0;
}
intcount=0;
//试探8个方向
for(inti=0;i<8;i++){
inttemI=nextX+movex[i];
inttemJ=nextY+movey[i];
//走到边界,路断
if(temI<0||temI>7||temJ<0||temJ>7){
continue;
}
//记录下可走的方向
if(0==board[temI][temJ]){
nextStepX[count]=temI;
nextStepY[count]=temJ;
count++;
}
}
//到这里,cout表示当前点有几种走法。
nextStep中存储各种走法的坐标。
intmin=-1;
if(count==0){
returnfalse;
}
if(1==count){
min=0;
}else{
for(inti=0;ifor(intj=0;j<8;j++)
{
inttemI=nextStepX[i]+movex[j];
inttemJ=nextStepY[i]+movey[j];
if(temI<0||temI>7||temJ<0||temJ>7){
continue;
}
//记录下这个位置可走的方向数
if(0==board[temI][temJ]){
exitS[i]++;
}
}
}
inttem=exitS[0];
min=0;
//从可走的方向中,寻找最少走的出路
for(inti=1;iif(tem>exitS[i]){
tem=exitS[i];
min=i;
}
}
}
//得到最少的出路
nextX=nextStepX[min];
nextY=nextStepY[min];
board[nextX][nextY]=m;
}
returntrue;
}
publicstaticvoidmain(String[]args){
intfirstX,firstY;
System.out.println("输入起始点(0-7):
");
Scannerscanner=newScanner(System.in);
firstX=scanner.nextInt();
firstY=scanner.nextInt();
int[][]board=newint[8][8];
Qishiknight=newQishi();
if(knight.Travel(firstX,firstY,board)){
System.out.println("游历完成:
");
}else{
System.out.println("游历失败!
\n");
}
for(inti=0;ifor(intj=0;jif(board[i][j]<10){
System.out.print(""+board[i][j]);
}else{
System.out.print(board[i][j]);
}
System.out.print("");
}
System.out.println();
}
}
}
实例:
2.行列变换问题的程序:
packagecom.t8;
importjava.util.LinkedList;
importjava.util.Scanner;
classgraph{
staticintsour,dest;//sour是图形的初始整数,dest是图形的目的整数
staticintans[]=newint[1<<16];//静态变量(即全局变量),用于存放图形变换的路径
intm=4,n=4,x;
introw[]=newint[4];
intcol[]=newint[4];
voidsetx(intx){
this.x=x;
}
intgetx(){
returnthis.x;
}
voidrowx(){//将一个整数划分成四行二进制
inty;
for(inti=0;iy=1;
row[i]=0;
for(intj=0;jif((x&1)!
=0)//如果x的最低位是1
row[i]|=y;
y<<=1;
x>>=1;
}
}
}
voidcolx(){//将一个整数划分成四列二进制
inty;
for(intj=0;jy=1;
for(inti=0;ifor(intj=0;jif((x&1)!
=0)//如果x的最低位是1
col[j]|=y;
x>>=1;
}
y<<=1;
}
}
voidrowy(){//将四行二进制转换成一个整数
intz=1,x=0,y;
for(inti=0;iy=row[i];
for(intj=0;jif((y&1)!
=0)//如果y的最低位是1
x|=z;
z<<=1;
y>>=1;
}
}
this.x=x;
}
voidcoly(){//将四列二进制转换成一个整数
intz=1,x=0,y;
for(inti=0;ifor(intj=0;jif((col[j]&1)!
=0)//如果y的最低位是1
x|=z;
z<<=1;
col[j]>>=1;
}
}
this.x=x;
}
voidSwaprow(inti,intj){//将二进数进行行互换
into;
o=row[i];
row[i]=row[j];
row[j]=o;
}
voidSwapcol(inti,intj){//将二进数进行列互换
into;
o=col[i];
col[i]=col[j];
col[j]=o;
}
voidreveR(intk){//将二进数进行行颠倒
inty=0,z=1<<(4-1);
for(intj=0;j<4;j++){
if((row[k]&1)!
=0)//如果y的最低位是1
y|=z;
z>>=1;
row[k]>>=1;
}
row[k]=y;
}
voidreveC(intk){//将二进数进行列颠倒
inty=0,z=1<<(4-1);
for(intj=0;j<4;j++){
if((col[k]&1)!
=0)//如果y的最低位是1
y|=z;
z>>=1;
col[k]>>=1;
}
col[k]=y;
}
introwswap(intx,inti,intj){//将二进制数的第i行与第j行互换
this.x=x;
rowx();
Swaprow(i,j);
rowy();
returnthis.x;
}
intcolswap(intx,inti,intj){//将二进制数的第i列与第j列互换
this.x=x;
colx();
Swapcol(i,j);
coly();
returnthis.x;
}
intrevrow(intx,intk){//将二进制数的第K行颠倒
this.x=x;
rowx();
reveR(k);
rowy();
returnthis.x;
}
intrevcol(intx,intk){//将二进制数的第K列颠倒
this.x=x;
colx();
reveC(k);
coly();
returnthis.x;
}
}
publicclassTuxing{
publicstaticvoidmain(String[]args){
finalintMaxsize=1<<16;
graphgN;//用于进行行变换、列变换、行颠倒、列颠倒
intE,N;//变换前的初始值,变换前的结果值
gN=newgraph();
inthash[]=newint[1<<16];
inti,j,h=0;charc;
graphG1=newgraph();
//初始化,输入初始值和目标值,即1010010000101010和1010000001100101
Scannerscanner=newScanner(System.in);
Stringss=scanner.nextLine();
char[]chArrs=ss.toCharArray();
for(graph.sour=i=0;i<16;i++){
c=chArrs[i];
graph.sour|=(int)(c-'0')<
}
Stringsd=scanner.nextLine();
char[]chArrd=sd.toCharArray();
for(graph.dest=i=0;i<16;i++){
c=chArrd[i];
graph.dest|=(int)(c-'0')<
}
LinkedListqueue=newLinkedList();//初始化先进先出队列
for(intk=0;kG1.x=graph.sour;
hash[G1.x]=0;
queue.add(G1.x);
while(!
queue.isEmpty()){//以先进先出队列式实现分支限界法
E=(int)queue.removeFirst();
for(i=0;i<4-1;i++){//行变换
for(j=i+1;j<4;j++){
gN.x=gN.rowswap(E,i,j);
N=gN.x;
if(hash[N]==-1){
hash[N]=hash[E]+1;
graph.ans[N]=E;
queue.add(N);
}
}
}
for(i=0;i<4-1;i++){//列变换
for(j=i+1;j<4;j++){
gN.x=gN.colswap(E,i,j);
N=gN.x;
if(hash[N]==-1){
hash[N]=hash[E]+1;
graph.ans[N]=E;
queue.add(N);
}
}
}
for(i=0;i<4;i++){//行颠倒
gN.x=gN.revrow(E,i);
N=gN.x;
if(hash[N]==-1){
hash[N]=hash[E]+1;
graph.ans[N]=E;
queue.add(N);
}
}
for(i=0;i<4;i++){//列颠倒
gN.x=gN.revcol(E,i);
N=gN.x;
if(hash[N]==-1){
hash[N]=hash[E]+1;
graph.ans[N]=E;
queue.add(N);
}
}
if(hash[graph.dest]!
=-1){//如果目的值被遍历到,则退出循环
System.out.println("OK");break;
}
}
System.out.println(hash[graph.dest]);
output(graph.dest);//输出变换的路径
}
publicstaticvoidoutb(intx){//将一个整数以四行二进制的形式显示
for(inti=0;i<4;i++){
for(intj=0;j<4;j++){
if((x&1)!
=0)System.out.print
(1);
elseSystem.out.print(0);
x/=2;
}
System.out.println();
}
}
publicstaticvoidoutput(intN){
if(N==graph.sour){
System.out.println();
outb(N);
return;
}
output(graph.ans[N]);//graph.ans[N]存放着从初始值到目的值的遍历路径
System.out.println();
outb(N);
}
}
实例:
总结
实验心得体会:
掌握回溯法解决问题的一般步骤。
学会使用回溯法解决实际问题。
掌握分支限界法解决问题的基本思想。
学会使用分支限界法解决实际问题
改进意见:
对于分支限界法了解的不透彻,应与老师沟通解决此问题,回溯法可以帮我们更好的解决一些多解的复杂性问题,所以,要更好的去学习和运用回溯法思想。
实验成绩:
指导教师:
年月日