1.骑士游历问题
问题描述:
在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次。若给定起始位置(x0,y0),编程探索出一条路径,沿着这条路径马能遍历棋盘上的所有单元格。
问题分析:
设当前马在棋盘的某个位置(x, y)上,按照规则,下一步有8个方向可走。设二维数组mat表示棋盘,每个元素表示棋盘的一格,其值定义如下:0表示马未到达过,Mat[i, j]= -1表示棋盘边界,自然数表示马到达该格的步数。
设计思想:
马从棋盘上的某一初始位置(x0,y0)开始,每次选择一个方向k,向前走一格,直到走完64格为止。每走一格,设置数组中相应格的元素值为马走的步数。 如果选择的方向k=0,表示可能的8种走向都试探不通,不通的原因是走向超出了棋盘范围,或当前位置已经被马访问过。此时马已无路可走,说明前几步走得不对,应该退回去重新换一种走法,这种逐步试探的设计思想称为回溯算法。 回溯算法在每一格上朝一个方向盲目地进行试探,遇到在某一格上所有方向都不能走通时,才回到前一格上来试探另一个方向。当每一格上的所有方向都试探过,不能走通时,才做出“走不通”的结论。因此该算法在探索时带有一定的盲目性和随机性,运行效率较低。
接下来上代码:
import java.util.*;
public class QS {
private boolean Travel(int firstX, int firstY, int[][] board) {
int[] movex = { -2, -1, 1, 2, 2, 1, -1, -2 };
int[] movey = { 1, 2, 2, 1, -1, -2, -2, -1 };
int[] nextStepX = new int[board.length];
int[] nextStepY = new int[board.length];
int[] exitS = new int[board.length];
int nextX = firstX;
int nextY = firstY;
board[nextX][nextY] = 1;
for (int m = 2; m <= Math.pow(board.length, 2); m++) {
for (int i = 0; i <board.length; i++) {
exitS[i] = 0;
}
int count = 0;
for (int i = 0; i < 8; i++) {
int temI = nextX + movex[i];
int temJ = 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++;
}
}
int min = -1;
if (count == 0) {
return false;
}
if (1 == count) {
min = 0;
}
else {
for (int i = 0; i < count; i++) {
for (int j = 0; j < 8; j++) {
int temI = nextStepX[i] + movex[j];
int temJ = nextStepY[i] + movey[j];
if (temI< 0 || temI> 7 || temJ< 0 || temJ> 7) {
continue;
}
if (0 == board[temI][temJ]) {
exitS[i]++;
}
}
}
int tem = exitS[0];
min = 0;
for (int i = 1; i < count; i++) {
if (tem >exitS[i]) {
tem = exitS[i];
min = i;
}
}
}
nextX = nextStepX[min];
nextY = nextStepY[min];
board[nextX][nextY] = m;
}
return true;
}
public static void main(String[] args) {
int firstX, firstY;
System.out.println("输入起始点(0-7):");
Scanner scanner = new Scanner(System.in);
firstX = scanner.nextInt();
firstY = scanner.nextInt();
int[][] board = new int[8][8];
QS knight = new QS();
if (knight.Travel(firstX, firstY, board)) {
System.out.println("游历完成:");
} else {
System.out.println("游历失败!\n");
}
for (int i = 0; i <board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] < 10) {
System.out.print(" " + board[i][j]);
} else {
System.out.print(board[i][j]);
}
System.out.print(" ");
}
System.out.println();
}
}
}
运行结果:
2.行列变换问题:
问题描述:
给定两个mn方格阵列组成的图形A和图形B,每个方格的颜色为黑色或白色,如下图所示。行列变换问题的每一步变换可以交换任意2行或2列方格的颜色,或者将某行或某列颠倒。上述每次变换算作一步。试设计一个算法,计算最少需要多少步,才能将图形A变换为图形B。
问题分析:
先进先出队列式分支限界法
在当前节点(扩展节点)处,先生成其所有的儿子节点(分支),然后再从当前的活节点(当前节点的子节点)表中选择下一个扩展节点。为了有效地选择下一个扩展节点,加速搜索的进程,在每一个活节点处,计算一个函数值(限界),并根据函数值,从当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。分支限界法解决了大量离散最优化的问题。
队列式分支限界法将活节点表组织成一个队列,并将队列的先进先出原则选取下一个节点为当前扩展节点。
算法步骤:
输入数据,将计算出的最少变换次数和相应的变换序列输出。第1 行是最少变换次数。从第2 行开始,每行用4 个数表示一次变换。
接下来上代码:
import java.util.LinkedList;
import java.util.Scanner;
class graph{
static int sour, dest;
static int ans[]=new int[1<<16];
int m=4,n=4,x;
int row[]=new int[4];
int col[]=new int[4];
void setx(int x){
this.x=x;
}
int getx(){
return this.x;
}
void rowx(){
int y;
for(int i=0;i<m;i++){
y=1;
row[i]=0;
for(int j=0;j<n;j++){
if((x&1)!=0)
row[i]|=y;
y<<=1;
x>>=1;
}
}
}
void colx(){
int y;
for(int j=0;j<n;j++) col[j]=0;
y=1;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if((x&1)!=0)
col[j]|=y;
x>>=1;
}
y<<=1;
}
}
void rowy(){
int z=1, x=0, y;
for(int i=0;i<m;i++){
y=row[i];
for(int j=0;j<n;j++){
if((y&1)!=0)
x|=z;
z<<=1;
y>>=1;
}
}
this.x=x;
}
void coly(){
int z=1, x=0, y;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if((col[j]&1)!=0)
x|=z;
z<<=1;
col[j]>>=1;
}
}
this.x=x;
}
void Swaprow(int i, int j){
int o;
o=row[i];
row[i]=row[j];
row[j]=o;
}
void Swapcol(int i, int j){
int o;
o=col[i];
col[i]=col[j];
col[j]=o;
}
void reveR(int k){
int y=0, z=1<<(4-1);
for(int j=0;j<4;j++){
if((row[k]&1)!=0)
y|=z;
z>>=1;
row[k]>>=1;
}
row[k]=y;
}
void reveC(int k){
int y=0, z=1<<(4-1);
for(int j=0;j<4;j++){
if((col[k]&1)!=0)
y|=z;
z>>=1;
col[k]>>=1;
}
col[k]=y;
}
int rowswap(int x, int i, int j){
this.x=x;
rowx();
Swaprow(i,j);
rowy();
return this.x;
}
int colswap(int x, int i, int j){
this.x=x;
colx();
Swapcol(i,j);
coly();
return this.x;
}
int revrow(int x, int k){
this.x=x;
rowx();
reveR(k);
rowy();
return this.x;
}
int revcol(int x, int k){
this.x=x;
colx();
reveC(k);
coly();
return this.x;
}
}
public class HL {
public static void main(String[] args){
final int Maxsize=1<<16;
graph gN;
int E=0;
int N=0;
gN=new graph();
int hash[]=new int[1<<16];
int i,j,h=0;char c;
graph G1=new graph();
Scanner scanner = new Scanner(System.in);
String ss=scanner.nextLine();
char[]chArrs=ss.toCharArray();
for(graph.sour=i=0;i<16;i++){
c=chArrs[i];
graph.sour|=(int)(c-'0')<<i;
}
String sd=scanner.nextLine();
char[]chArrd=sd.toCharArray();
for(graph.dest=i=0;i<16;i++){
c=chArrd[i];
graph.dest|=(int)(c-'0')<<i;
}
LinkedList queue=new LinkedList();
for(int k=0; k<Maxsize;k++)hash[k]=-1;
G1.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);
}
public static void outb(int x){
for(int i=0; i<4;i++){
for(int j=0;j<4;j++){
if((x&1)!=0)System.out.print(1);
else System.out.print(0);
x/=2;
}
System.out.println();
}
}
public static void output(int N){
if(N==graph.sour){
System.out.println();
outb(N);
return;
}
output(graph.ans[N]);
System.out.println();
outb(N);
}
}
运行结果: