執行緒池之FixedThreadPool
阿新 • • 發佈:2021-02-06
技術標籤:hdu/poj
題目描述
在一個3*3的棋盤上放置編號為1-8的8個方塊,每個佔一格,另外還有一個空格。與空格相鄰的數字方塊可以移動到空格里。任務1:指定初始棋局和目標棋局,計算出最少的移動步數;任務2:輸出數碼的移動序列。
把空格看成0,一共有9個數字。
輸入樣例:
1 2 3 0 8 4 7 6 5
1 0 3 8 2 4 7 6 5
輸出樣例:
2
題目分析
這裡先解決任務1,任務2是在任務1的基礎上通過標記序列和回溯去解決的。
把一個棋局看成一個狀態圖,總共有9!= 362880個狀態。從初始棋局開始,每次移動轉到下一個狀態,到達目標棋局停止。八數碼問題其實是一個經典的BFS問題。八數碼從初始狀態出發,每次轉移逐步逼近目標狀態。每轉移一次,步數加1,當達到目標時,經過的步數就是最短路徑。
八數碼的重要問題其實是判重。對於已經訪問過的狀態要進行標記,防止重複訪問。這裡可以用數學方法“康託展開”來判重。
至於“康託展開”是什麼,可以參考以下部落格。
都能看懂的康託展開
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int LEN=362880;
struct node{
int state[9];//記錄一個八數碼排列,即一個狀態
int dis;//記錄到起點的距離
};
int dir[4][2]={
{-1,0},
{0,-1},
{1,0},
{0,1}
};//左、上、右、下的順時針方向
int vis[ LEN];
int start[9],goal[9];
long int factory[]={1,1,2,6,24,120,720,5040,40320,362880};//0-9的階乘
//用康託展開判重
int Cantor(int str[],int n)
{
long result;
for(int i=0;i<n;i++){
int count=0;
for(int j=i+1;j<n;j++){
if(str[i]>str[j])
count++;
}
result+=count*factory[n-i-1];
}
if(!vis[result]){
vis[ result]=1;
return 1;
}
else
return 0;
}
int bfs()
{
queue<node> Q;
node head;
head.dis=0;
//複製起點的狀態
memcpy(head.state,start,sizeof(head.state));
if(Cantor(head.state,9))
Q.push(head);
while(!Q.empty()){
head=Q.front();
//到達目標,返回距離
if(memcmp(head.state,goal,sizeof(goal))==0){
return head.dis;
}
Q.pop();
int z;
//找到0的位置
for(z=0;z<9;z++){
if(head.state[z]==0){
break;
}
}
int x,y;
//橫、縱座標
x=z%3;y=z/3;
for(int i=0;i<4;i++){
int newx,newy;
newx=x+dir[i][0];
newy=y+dir[i][1];
//轉為一維
int nz=newy*3+newx;
if(newx>=0&&newx<3&&newy>=0&&newy<3){//未越界
node newnode;
memcpy(&newnode,&head,sizeof(struct node));
//把0移動到新的位置
swap(newnode.state[z],newnode.state[nz]);
newnode.dis++;
if(Cantor(newnode.state,9)){
Q.push(newnode);
}
}
}
}
return -1;//沒找到
}
int main()
{
for(int i=0;i<9;i++) cin>>start[i];
for(int i=0;i<9;i++) cin>>goal[i];
int num=bfs();
if(num!=-1) cout<<num<<endl;
else cout<<"Impossible"<<endl;
return 0;
}
更多
關於任務2,輸出數碼的移動序列,也就是如何記錄移動過程的操作,如何記錄、回溯,可以參考以下部落格裡面的例題。
Eight