1. 程式人生 > >10.9-分支界限法(//跳馬//獨輪車//六數碼問題//找倍數//八數碼問題)

10.9-分支界限法(//跳馬//獨輪車//六數碼問題//找倍數//八數碼問題)

1.跳馬

描述:在國際象棋中,馬的走法與中車象棋類似,即俗話說的“馬走日”,下圖所示即國際象棋中馬(K)在一步能到達的格子(其中黑色的格子是能到達的位置)。

現有一200*200大小的國際象棋棋盤,棋盤中僅有一個馬,給定馬的當前位置(S)和目標位置(T),求出馬最少需要多少跳才能從當前位置到達目標位置。

輸入:本題包含多個測例。輸入資料的第一行有一個整數N(1<=N<=1000),表示測例的個數,接下來的每一行有四個以空格分隔的整數,分別表示馬當前位置及目標位置的橫、縱座標C(x,y)和G(x,y)。座標由1開始。

輸出:對於每個測例,在單獨的一行內輸出一個整數,即馬從當前位置跳到目標位置最少的跳數。

輸入樣例

2
1 1 2 1
1 5 5 1

輸出樣例

3
4

#include<iostream>
#include<queue>
 
using namespace std;
 
int cx,cy,gx,gy;
int a[201][201]={0};
int step[201][201];
//馬走的方向所對應的行列變化 
int b[8]={-2,-2,-1,-1,1,1,2,2};
int c[8]={-1,1,-2,2,-2,2,-1,1};
 
//結構體內定義佇列 
struct add
{
 queue<int>q1;
 queue<int>q2;
}add;
 
int search(int x,int y);
 
int main()
{
 int n,i,j,k=0;
 cin>>n;
 int ans[n];
 while(k<n)
 {
  cin>>cx>>cy>>gx>>gy;
  //初始化 
  step[cx][cy]=0;
  a[cx][cy]=1;
  add.q1.push(cx);
  add.q2.push(cy);
  ans[k++]=search(cx,cy);
  //清空棋盤和佇列,防止下一組測試受到影響 
  for(i=0;i<201;i++)
     {
      for(j=0;j<201;j++)
      {
       a[i][j]=0;
       step[i][j]=0; 
      }
     }
     while(!add.q1.empty())
     {
      add.q1.pop();
     }
     while(!add.q2.empty())
        {
      add.q2.pop();
     }
 }
 for(i=0;i<n;i++)
 {
  cout<<ans[i]<<endl;
 } 
 return 0;
}
 
int search(int x,int y)
{
 int xt,yt,e,f,i;
 while(1)
 {
  e=add.q1.front();
  add.q1.pop();
  f=add.q2.front();
     add.q2.pop();
  for(i=0;i<8;i++)
     {
       xt=e+b[i];
      yt=f+c[i];
      if(xt==gx&&yt==gy)
      {
        return(step[e][f]+1);
      }
      if(xt<1||xt>200||yt<1||yt>200) continue;//剪枝陣列越界的情況 
      if(a[xt][yt]==0)
      {//處理下一組可能的情況 
       a[xt][yt]=1;
       step[xt][yt]=step[e][f]+1;
       add.q1.push(xt);
       add.q2.push(yt);
      }
     }
 }
}

2.獨輪車

描述:獨輪車的輪子上有紅、黃、藍、白、綠(依順時針序)5種顏色,在一個如下圖所示的20*20的迷宮內每走一個格子,輪子上的顏色變化一次。獨輪車只能向前推或在原地轉向。每走一格或原地轉向90度均消耗一個單位時間。現給定一個起點(S)和一個終點(T),求獨輪車以輪子上的指定顏色到達終點所需的最短時間。

輸入:本題包含一個測例。測例中分別用一個大寫字母表示方向和輪子的顏色,其對應關係為:E-東、S-南、W-西、N-北;R-紅、Y-黃、B-藍、W-白、G-綠。在測試資料的第一行有以空格分隔的兩個整數和兩個大寫字母,分別表示起點的座標S(x,y)、輪子的顏色和開始的方向,第二行有以空格分隔的兩個整數和一個大寫字母,表示終點的座標T(x,y)和到達終點時輪子的顏色,從第三行開始的20行每行內包含20個字元,表示迷宮的狀態。其中'X'表示建築物,'.'表示路.

輸出:在單獨的一行內輸出一個整數,即滿足題目要求的最短時間。

輸入樣例

3 4 R N
15 17 Y
XXXXXXXXXXXXXXXXXXXX
X.X...XXXXXX......XX
X.X.X.....X..XXXX..X
X.XXXXXXX.XXXXXXXX.X
X.X.XX....X........X
X...XXXXX.X.XX.X.XXX
X.X.XX....X.X..X.X.X
X.X.X..XX...XXXX.XXX
X.X.XX.XX.X....X.X.X
X.X....XX.X.XX.X.X.X
X.X.X.XXXXX.XX.X.XXX
X.X.X.XXXXX....X...X
X.X.......X.XX...X.X
X.XXX.XXX.X.XXXXXXXX
X.....XX.......X...X
XXXXX....X.XXXXXXX.X
X..XXXXXXX.XXX.XXX.X
X.XX...........X...X
X..X.XXXX.XXXX...XXX
XXXXXXXXXXXXXXXXXXXX

輸出樣例

56

#include<iostream>
#include<queue>
using namespace std;

char map[20][20];
bool canuse[20][20][5][6];
int step[20][20][5][6];
int tx, ty, tcolor;
int ans;
int gorow[4] = {-1, 0, 1, 0};
int gocol[4] = {0, 1, 0, -1};

struct horse{
	queue <int> row;
	queue <int> col;
	queue <int> dire;
	queue <int> color;
}horse;

void init();
int search();
int change(char c);

int main(){
    init();
	ans = search();
	cout << ans <<endl;
	return 0;
}

void init(){
	int i, j, k, l;
	int cx, cy;
    char cdir;
    char ccol;
    char tcol;
    cin >> cx >> cy >> ccol >> cdir;
	cin >> tx >> ty >> tcol;
	for(i = 0; i < 20; i++){
		for(j = 0; j < 20; j++){
		    cin >> map[i][j];
			if(map[i][j] == 'X'){
				for(k = 0; k < 4; k++){
					for(l = 0; l < 5; l++){
					    canuse[i][j][k][l] = false;
					}
				}
			}
			else{
				for(k = 0; k < 4; k++){
					for(l = 0; l < 5; l++){
                        canuse[i][j][k][l] = true;
					}
				}
			}
		}
	}
	tcolor = change(tcol);
	step[cx-1][cy-1][change(cdir)][change(ccol)] = 0;
	horse.row.push(cx-1);
	horse.col.push(cy-1);
	horse.dire.push(change(cdir));
	horse.color.push(change(ccol));
	canuse[cx-1][cy-1][change(cdir)][change(ccol)] = false; 
}

int change(char c){
	if(c == 'N') return 0;
	if(c == 'E') return 1;
	if(c == 'S') return 2;
	if(c == 'W') return 3;
	if(c == 'R') return 0;
	if(c == 'Y') return 1;
	if(c == 'B') return 2;
	if(c == 'W') return 3;
	if(c == 'G') return 4;
}

int search(){
	int nx, ny, vx, vy;
	int ndire, ncolor, vdire, vcolor;
	int i;
	while(1){
	    nx = horse.row.front();
		ny = horse.col.front();
		ndire = horse.dire.front();
		ncolor = horse.color.front();
		horse.row.pop();
		horse.col.pop();
		horse.dire.pop();
		horse.color.pop();
		for(i = 0; i < 3; i++){  //0表示順時針轉向,1表示逆時針轉向,2表示向前走一步
			if(i == 0){
			    vcolor = ncolor;
				vdire = (ndire+1)%4;
				vx = nx;
				vy = ny;
			}
			else if(i == 1){
			    vcolor = ncolor;
				vdire = (ndire+3)%4;
				vx = nx;
				vy = ny;
			}
			else{
                vcolor = (ncolor+1)%5;
				vdire = ndire;
				vx =nx + gorow[ndire];
				vy =ny + gocol[ndire];
			}
           if(vx == tx-1 && vy == ty-1 && vcolor == tcolor){
		        return(step[nx][ny][ndire][ncolor] + 1);
		   }
		   if(vx >= 0 && vx < 20 && vy >= 0 && vy < 20 && canuse[vx][vy][vdire][vcolor]){
		       horse.row.push(vx);
			   horse.col.push(vy);
			   horse.dire.push(vdire);
			   horse.color.push(vcolor);
			   canuse[vx][vy][vdire][vcolor] = false;
			   step[vx][vy][vdire][vcolor] = step[nx][ny][ndire][ncolor] + 1; 
		   }
		}	
	}
}

3.六數碼問題

描述:現有一兩行三列的表格如下:

A B C
D E F

把1、2、3、4、5、6六個數字分別填入A、B、C、D、E、F格子中,每個格子一個數字且各不相同。每種不同的填法稱為一種佈局。如下:

1 3 5
2 4 6
佈局1

2 5 6
4 3 1
佈局2

定義α變換如下:把A格中的數字放入B格,把B格中的數字放入E格,把E格中的數字放入D格,把D格中的數字放入A格。
定義β變換如下:把B格中的數字放入C格,把C格中的數字放入F格,把F格中的數字放入E格,把E格中的數字放入B格。

問:對於給定的佈局,可否通過有限次的α變換和β變換變成下面的目標佈局:

1 2 3
4 5 6
輸入:本題有多個測例,每行一個,以EOF為輸入結束標誌。每個測例的輸入是1到6這六個數字的一個排列,空格隔開,表示初始佈局ABCDEF格中依次填入的數字。

輸出:每個輸出佔一行。可以轉換的,列印Yes;不可以轉換的,列印No。

輸入樣例

1 3 5 2 4 6
2 5 6 4 3 1

輸出樣例

No
Yes

提示:第二個示例即佈局2的一種轉換方法:αααβαα

#include<iostream>
#include<math.h>
#include<queue>
#include<map>
using namespace std;

queue <int> q;
map <int, int> used;

int pow(int x, int y);
int bfs();
int moveto(int v, int i);

int main(){
	int num, tmp;
	while(cin >> tmp){
		num = tmp*pow(10,5);
		for(int i = 4; i >= 0; i--){
		    cin >> tmp;
			num += tmp*pow(10,i); 
		}

		while(!q.empty()){
		    q.pop();
		}
		used.clear();

		q.push(num);
		used[num] = 1;

		if(bfs()){
		    cout << "Yes" <<endl;
		}
		else{
		    cout << "No" <<endl;
		}
	}
}

int bfs(){
	int u, v;
	while(!q.empty()){
	    u = q.front();
		q.pop();
		for(int i = 0; i < 2; i++){    //i==0做α變換,i==1做 β變換 
		    v = moveto(u, i);
            if(u == 123456){
		        return 1;
		    }
			if(used[v] == 0){
			    q.push(v);
				used[v] = 1;
			}
		}
	}
	return 0;
}

int pow(int x, int y){
	int num;
	num = x; 
	if(y == 0){
		return 1;
	}
	for(int i = 0; i < y-1; i++){
	    num *= x;
	} 
	return num;
}

int moveto(int v, int i){
	int a, b, c, d, e, f, tmp;
	f = v%10; v = v/10;
	e = v%10; v = v/10;
	d = v%10; v = v/10;
	c = v%10; v = v/10;
	b = v%10; v = v/10;
	a = v%10;
	if(i == 0){
	    tmp = a;
		a = d;
		d = e;
		e = b;
		b = tmp;
	}
	else{
	    tmp = b;
		b = e;
		e = f;
		f = c;
		c = tmp;
	}
	for(int i = 0; i < 6; i++){
	    return(a*pow(10,5)+b*pow(10,4)+c*pow(10,3)+d*pow(10,2)+e*pow(10,1)+f);
	}
}

4.找倍數

描述:對於每個輸入的數字(如:2),則要求 給出一個由1,0構成的十進位制整數,且該整數為輸入數字的某個倍數,且是滿足該條件的最小數(如2對應的10)。

輸入:數字n,n等於0時停止。

輸出:n的一個滿足條件的最小倍數。

輸入樣例

2
0

輸出樣例

10

#include<iostream>
#include<queue>
using namespace std;

long long n;

queue <long long> q;

void search();

int main(){
	while(cin >> n && n != 0){
		if(n == 1){
		    cout << '1' << endl;
		}
		else{
		    while(!q.empty()){
		        q.pop();
		    }
		    q.push(1);
		    search();
		}
	}
	return 0;
}

void search(){
    long long u, v;
	int flag = 0;
	while(!q.empty()){
		if(flag){
		    break;
		}
	    u = q.front();
	    q.pop();
		for(int i = 0; i < 2; i++){
		    v = u*10 + i;
			if(v % n == 0){
			    cout << v << endl;
				flag = 1;
				break;
			}
			else{
			    q.push(v);
			}
		}
	}
}

5.八數碼問題

描述:在九宮格里放在1到8共8個數字還有一個是空格,與空格相鄰的數字可以移動到空格的位置,問給定的狀態最少需要幾步能到達目標狀態(用0表示空格):
1 2 3
4 5 6
7 8 0

輸入:輸入一個給定的狀態。

輸出:輸出到達目標狀態的最小步數。不能到達時輸出-1。

輸入樣例:

1 2 3
4 0 6
7 5 8

輸出樣例:

2

#include<iostream>
#include<queue>
#include<map>

using namespace std;

queue <int> q;
map <int, int> step;
map <int, int> used;

void init();
int bfs();
bool canmove(int u, int i);
int move(int u, int i);

int tx[4] = {0, 1, 0, -1};
int ty[4] = {1, 0, -1, 0};

int main(){
    init();
	cout << bfs() << endl;
	return 0;
}

void init(){
    int i;
	int tmp, num;
	cin >> tmp;
	num = tmp;
	for(i = 0; i < 8; i++){
	    cin >> tmp;
		num = num*10 + tmp;
	}
	step.clear();
	used.clear();
	q.push(num);
	step[num] = 0;
	used[num] = 1;
}

int bfs(){
	int i;
    int u, v;
	u = q.front();
	if(u == 123456780){
	    return 0;
	}
	while(!q.empty()){
	    u = q.front();
		q.pop();
		for(i = 0; i < 4; i++){  //分別向右,下,左,上交換
			if(canmove(u, i)){
			    v = move(u, i);
				if(v == 123456780){
				    return(step[u] + 1);
				}
				if(used.count(v) == 0){
				    q.push(v);
					step[v] = step[u] + 1;
					used[v] = 1;
				}
			}
		}
	}
	return -1;
}

bool canmove(int u, int i){
    int arr[3][3];
	int  j, k;
	int row, col;
	int vx, vy;
	for(j = 2; j >= 0; j--){
		for(k = 2; k >= 0; k--){
		    arr[j][k] = u%10;
			u = u/10;
			if(arr[j][k] == 0){
			    row = j;
				col = k;
			}
		}
	}
	vx = row + tx[i];
	vy = col + ty[i];
	if(vx >= 0 && vx < 3 && vy >= 0 && vy < 3){
	    return true;
	}
	else{
	    return false;
	}
}

int move(int u, int i){
    int arr[3][3];
	int j, k;
	int row, col;
	int vx, vy;
	int num;
	for(j = 2; j >= 0; j--){
		for(k = 2; k >= 0; k--){
		    arr[j][k] = u%10;
			u = u/10;
			if(arr[j][k] == 0){
			    row = j;
				col = k;
			}
		}
	}
	vx = row + tx[i];
	vy = col + ty[i];
	arr[row][col] = arr[vx][vy];
	arr[vx][vy] = 0;
	num = 0;
	for(j = 0; j < 3; j++){
		for(k = 0; k < 3; k++){
		    num = num*10 + arr[j][k];
		}
	}
	return num;
}