生活随笔
收集整理的這篇文章主要介紹了
搜索训练1 [8数码问题]
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
HDU1043、以及POJ1077上面都有這道題目,可以說是搜索里的非常經(jīng)典的題目了。
poj上面的數(shù)據(jù)真的是弱,由于只有一組數(shù)據(jù),簡單bfs直接就可以過掉。
前前后后搗鼓了能有6個小時,才把這道題目在HDU上以4500ms的微弱優(yōu)勢通過。。。。。。。
我的思路一開始是簡單的bfs,在HDU上超時。然后改用A*搜索,嘗試了3種啟發(fā)函數(shù),才微弱的過掉。。。。我看別人的代碼也是A*算法,而且啟發(fā)函數(shù)寫的跟我一樣,為什么就幾百ms過掉了呢。。。。還是不明白。。。。
思路:
(1)本題比較關鍵的一點就是判重問題,怎么樣保證同一個狀態(tài)只能被訪問一次,涉及到狀態(tài)的表示問題。
而狀態(tài)其實是一個排列數(shù),我們現(xiàn)在想要把一個排列數(shù)Hash到一個整數(shù)上,這就用到了康托展開的方法。(見我前一個博文)
(2)第二個比較關鍵的地方就是啟發(fā)函數(shù)的選取
啟發(fā)函數(shù)設置了兩個參數(shù):
val:當前狀態(tài)到目標狀態(tài)的哈密頓距離。
step: 當前已經(jīng)移動的次數(shù)。
函數(shù)1:val + step :在poj上以67ms通過,在HDU上TLE
函數(shù)2:step為第一關鍵字,val為第二關鍵字 在poj上以760ms通過,在HDU當然TLE
函數(shù)3:以val為第一關鍵字,以step為第二關鍵字,在poj上以0ms通過,在HDU上以4500ms通過
這里還有一個需要注意的地方,就是無解情況的判定:
有一個定理,當兩個狀態(tài)的逆序數(shù)的奇偶性相同的時候,他們可以互相到達。
否則,他們無法互相到達,這個定理可以快速完成無解判定。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
class cantor{public:int fac_dp[11];int fac(int i){if(fac_dp[i]) return fac_dp[i];return fac_dp[i] = i*fac(i-1);}void init(){memset(fac_dp,0,sizeof(fac_dp));fac_dp[0] = 1;fac(10);}int encode(int num[],int n){int ans = 0;for(int i = 0;i < n;i++){int cnt = 0;for(int j = i;j < n;j++){if(num[i] > num[j])cnt++;}ans += fac_dp[n-i-1] * cnt;}return ans;}void decode(int ans[],int num,int n){int used[11];for(int i = 0;i < n;i++) used[i] = 0;for(int i = 0;i < n;i++){int cnt = num/fac_dp[n-i-1];int r = num%fac_dp[n-i-1];for(int j = 0;j < n;j++){if(!cnt && !used[j]){used[j] = 1;ans[i] = j;break;}if(!used[j]){cnt--;}}num = r;}}
};
struct node{int key;int val;int step;int loc;int x[9];friend bool operator<(node n1,node n2){if(n1.val == n2.val){return n1.step > n2.step;}return n1.val > n2.val;}node(int a,int b,int c,int loc):key(a),val(b),step(c),loc(loc){}
};
int a[11],b[11];
const int MAX = 3628800;
int pre[MAX];
int preid[MAX];
int visited[MAX];
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,-1,1};
int tarmp[9] = {8,0,1,2,3,4,5,6,7};
char dc[4] = {'d','u','l','r'};
cantor ct;
void prtpath(int end,int beg){char stk[100];int cnt = 0;while(end != beg){stk[cnt++] = dc[pre[end]]; end = preid[end];}while(cnt){putchar(stk[--cnt]);}puts("");
}
bool check(int arr[]){int sum = 0;for(int i = 0;i < 9;i++){if(arr[i] == 0) continue;for(int j = i;j < 9;j++){if(arr[j] == 0) continue;if(arr[i] > arr[j]) sum++;}}return sum%2 == 0;
}
int calc(int arr[]){int ans = 0;for(int i = 0;i < 9;i++){ans += abs(i/3 - tarmp[arr[i]]/3) + abs(i%3 - (tarmp[arr[i]]%3));}return ans;
}
int main(){ ct.init();char c;while(~scanf(" %c",&c)){memset(visited,0,sizeof(visited));memset(pre,0,sizeof(pre));if(c == 'x') a[0] = 0;else a[0] = c - '0';for(int i = 1;i < 9;i++){ scanf(" %c",&c);if(c == 'x'){a[i] = 0;}else{a[i] = c - '0';}}if(!check(a)){puts("unsolvable");continue;}for(int i = 0;i < 8;i++) b[i] = i+1;b[8] = 0;int tar = ct.encode(b,9);int code = ct.encode(a,9);preid[code] = code;priority_queue<node> Q; int lc = -1;while(a[++lc] != 0);node cur = node(code,calc(a),0,lc);for(int i = 0;i < 9;i++) cur.x[i] = a[i];Q.push(cur);visited[code] = 1;int f = 0;while(!Q.empty()){node nn = Q.top();Q.pop();int ccd = nn.key;if(ccd == tar){//找到了! //cout<<"YES"<<endl;prtpath(tar,code);f = 1;break;}int lc = nn.loc;int x = lc / 3;int y = lc % 3;nn.step ++;for(int i = 0;i < 4;i++){int nx = dx[i] + x;int ny = dy[i] + y;if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){int nlc = nx * 3 + ny;swap(nn.x[lc],nn.x[nlc]);int ncd = ct.encode(nn.x,9);if(!visited[ncd]){pre[ncd] = i;preid[ncd] = ccd;nn.key = ncd;nn.val = calc(nn.x);nn.loc = nlc;Q.push(nn);visited[ncd] = 1;}swap(nn.x[lc],nn.x[nlc]);}}}if(!f){puts("unsolvable");}}
}
總結
以上是生活随笔為你收集整理的搜索训练1 [8数码问题]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。