1. 程式人生 > 實用技巧 >[kuangbin帶你飛]專題二 搜尋進階

[kuangbin帶你飛]專題二 搜尋進階

[kuangbin帶你飛]專題二 搜尋進階


A - Eight HDU - 1043

這一題就是經典八數碼。演算法難點在於:

1.判重:map,hash,cantor康託展開

2.搜尋:bfs dbfs A*

我是直接暴力預處理,從12345678X開始進行所有情況處理,然後結果反序輸出。節點用int儲存9位數的序列。

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

using namespace std;

map<int,string
> ans; int n[10]; struct node{ int num,nine; node(int nn,int nni):num(nn),nine(nni) {}; }; int change(int a,int b,int c){ int u = c/n[a]%10, v = c/n[b]%10; //cout<<a<<" "<<b<<" "<<c<<endl; //cout<<u<<" "<<v<<" "<<u*n[a]<<" "<<v*n[b]<<" "<<v*n[a]<<" "<<u*n[b]<<endl;
return c - u*n[a] - v*n[b] + v*n[a] + u*n[b]; } void init(){ n[8] = 1;for(int i=7;i>=0;i--) n[i] = n[i+1]*10; //for(int i=0;i<9;i++) cout<<n[i]<<endl; queue<node> q; ans[123456789] = ""; q.push(node(123456789,8)); while(!q.empty()){ node p
= q.front();q.pop(); int num = p.num , nine = p.nine; int tnt; //--------------------------------------------- switch(nine){ case 0: tnt = change(0,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,1)); } tnt = change(0,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,3)); } break; case 1: tnt = change(1,0,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,0)); } tnt = change(1,2,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,2)); } tnt = change(1,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,4)); } break; case 2: tnt = change(2,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,1)); } tnt = change(2,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,5)); } break; case 3: tnt = change(3,0,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,0)); } tnt = change(3,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,4)); } tnt = change(3,6,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,6)); } break; case 4: tnt = change(4,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,1)); } tnt = change(4,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,3)); } tnt = change(4,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,5)); } tnt = change(4,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,7)); } break; case 5: tnt = change(5,2,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,2)); } tnt = change(5,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,4)); } tnt = change(5,8,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,8)); } break; case 6: tnt = change(6,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,3)); } tnt = change(6,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,7)); } break; case 7: tnt = change(7,6,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,6)); } tnt = change(7,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,4)); } tnt = change(7,8,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,8)); } break; case 8: tnt = change(8,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,5)); } tnt = change(8,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,7)); } break; default:cout<<nine<<endl; } //--------------------------------------------- } } int main(){ init(); char _; while(cin>>_){ int cnt=0; if(_=='x') _='9'; cnt+=(_-'0')*n[0]; for(int i=1;i<9;i++) { cin>>_; if(_=='x') _='9'; cnt+=(_-'0')*n[i]; } //cout<<cnt<<endl; //cout<<ans[cnt]<<endl; if(ans.count(cnt)==0) puts("unsolvable"); else{ string strng = ans[cnt]; //cout<<strng<<endl; for(int i=strng.length()-1;i>=0;i--){ cout<<strng[i]; }cout<<endl; } } return 0; }
View Code

B - Eight II HDU - 3567

這裡是要求是給出 起點和終點序列 然後得出最短操作 而且相同長度要求最小字典順序(重點,我這裡wrong了很多次 看題目才知道)

第一遍:就是A* + map 嘗試 然後 timelimit。

第二遍:看其他人程式碼,有兩種,一種就是設立limit的dfs,


    另一種就是預處理 ,這裡很巧妙 我想了一早上 想通了;重點 x 是操作單位不能動,其他字元是可替換的。

    舉例子:645X312 ---->456X123 的操作 和 123X456---->231X564的操作一樣 為什麼?因為這只是簡單字元替換 ,這種替換並不影響結果。甚至645X312可以替換成qweXyui,不影響的。

    然後就是12345678X,1234567X8....9種X在不同位置的預處理,然後通過給出起點X位置進行得出結果。

    重點 要 {dlru}這樣進行遍歷 因為需要最小字典序列

#include <iostream>
#include <queue>
#include <cstring>
#include <string>

using namespace std;

const int M = 400000;

int vis[9][M];
int pre[9][M];
//-----------------------------------------------------------
void show(int *a){
    for(int i=0;i<9;i++) cout<<a[i];cout<<endl;
}
//------------------------------------------------------------
int changeId[9][4] = {{3,-1,1,-1},{4,0,2,-1},{5,1,-1,-1},
                        {6,-1,4,0},{7,3,5,1},{8,4,-1,2},
                        {-1,-1,7,3},{-1,6,8,4},{-1,7,-1,5}};
char d[5] = {'d','l','r','u'};

//康託展開======================================================
const int fk[10] = {1,1,2,6,24,120,720,5040,40320,362880};
int cantor(int *a){
    int ans = 0;
    for(int i=0;i<8;i++){
        int small = 0;
        for(int j=i+1;j<9;j++) if(a[j]<a[i]) small++;
        ans += small*fk[8-i];
    }
    return ans;
}
//----------------------------------------------------------
struct node{
    int num[9],zero,hashcode;
};

void bfs(int x){

    node now;
    for(int i=0,j=1;i<9;i++)
        if(i==x) now.num[i] = 0;
        else now.num[i] = j++;
    now.zero=x;
    now.hashcode = cantor(now.num);

    queue<node> q;
    q.push(now);
    vis[x][now.hashcode] = 1;

    while(!q.empty()){
        node p = q.front();q.pop();
        int zero = p.zero,hashcode = p.hashcode;

        for(int i=0;i<4;i++){
            if(changeId[zero][i] == -1) continue;
            node temp = p;
            temp.zero = changeId[zero][i];
            swap(temp.num[zero],temp.num[temp.zero]);
            temp.hashcode = cantor(temp.num);
            if(vis[x][temp.hashcode]==-1){
                vis[x][temp.hashcode] = i;
                pre[x][temp.hashcode] = hashcode;
                q.push(temp);
            }
        }
    }
}
//預處理=================================================
void init(){
    memset(vis,-1,sizeof vis);
    memset(pre,-1,sizeof pre);
    for(int i=0;i<9;i++) bfs(i);
}
//---------------------------------------------------------
int main(){
    init();
    int _,op=1;
    scanf("%d",&_);
    while(_--){
        //----------------------
        int k;
        char a[10],b[10];
        int c[10],u[10];
        scanf("%s%s",a,b);
        //printf("%s\n%s\n",a,b);

        //進行符號兌換:c陣列為兌換陣列,得到兌換後的目標陣列和X位置========
        for(int i=0,j=1;i<9;i++)
            if(a[i]=='X') k=i;
            else c[a[i]-'0'] = j++;
        for(int i=0;i<9;i++)
            if(b[i]=='X') u[i] = 0;
            else u[i] = c[b[i]-'0'];

            //show(u);
        //===============================
        int can = cantor(u);
        string ans = "";
        while(can != -1){
            ans = d[vis[k][can]] +  ans;
            can = pre[k][can];
        }

        printf("Case %d: %d\n",op++,ans.length()-1);
        cout<<ans.substr(1)<<endl;
    }
    return 0;
}
View Code