• 八数码问题/搜索小结


    cdcq讲的挺好的其实。之所以没有回应是因为真的想不出了。

    八数码问题

    这个问题可以用BFS和A*解决。

    BFS

    考虑按空格的位置搜索,每次操作相当于移动空格。

    此处需要记忆化,不然会T飞。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<queue>
     6 #include<map>
     7 using namespace std;
     8 int n;
     9 int c[4][4];
    10 queue<int> q;
    11 map<int,int> mp;
    12 
    13 int k[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    14 
    15 inline bool check(int x,int y){
    16     if(x<1||x>3||y<1||y>3)return false;
    17     return true;
    18 }
    19 
    20 inline int rebiu(){
    21     int ans=0;
    22     for(int i=1;i<=3;i++)
    23         for(int j=1;j<=3;j++)
    24         ans=ans*10+c[i][j];
    25     return ans;
    26 }//将矩阵转化成数 
    27 
    28 inline void bfs(int now){
    29     q.push(now);
    30     mp[now]=0;
    31     while(!q.empty()){
    32         int lr,ud;//存储空格的位置 
    33         int x=q.front();q.pop();
    34         if(x==123804765){
    35             break;
    36         }
    37         int shp=x;
    38         for(int i=9;i>=1;--i){
    39             int k=shp%10;shp/=10;
    40             c[(i-1)/3+1][((i-1)%3)+1]=k;
    41             if(k==0) ud=(i-1)/3+1,lr=((i-1)%3)+1;
    42         }//恢复成矩阵,swap要用的 
    43         
    44         for(int i=0;i<4;++i){
    45             int dx=ud+k[i][0],dy=lr+k[i][1];
    46             if(!check(dx,dy))continue;
    47             swap(c[ud][lr],c[dx][dy]);
    48             int val=rebiu();
    49             if(!mp.count(val))
    50             mp[val]=mp[x]+1,
    51             q.push(val);
    52             swap(c[ud][lr],c[dx][dy]);
    53         }
    54     }
    55     
    56 }
    57 
    58 
    59 int main(){
    60     scanf("%d",&n);
    61     bfs(n);
    62     cout<<mp[123804765];
    63 }

    双向BFS

    双向BFS的特点:当前状态和目标状态都明确。

    双向BFS大大降低了搜索层数,所以快的飞起。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<queue>
     6 #include<map>
     7 using namespace std;
     8 int n;
     9 int c[4][4];
    10 map<int,int> v;//被哪个点扫到 
    11 map<int,int> ans;
    12 queue<int> q;
    13 int finl;
    14 int end=123804765;
    15 int dr[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    16 
    17 inline int rebiu(){
    18     int ans=0;
    19     for(int i=1;i<=3;i++)
    20         for(int j=1;j<=3;j++)
    21         ans=ans*10+c[i][j];
    22     return ans;
    23 }
    24 
    25 inline void bfs(int now){
    26     int flag=0;
    27     if(now==end)return;
    28     q.push(now);q.push(end);
    29     v[now]=1;v[end]=2;ans[now]=0;ans[end]=1;//ans[end]是要等于1的。 
    30     while(!q.empty()){
    31         int x=q.front();q.pop();
    32         int k=x,px,py;
    33         for(int i=9;i>=1;i--){
    34             c[(i-1)/3+1][(i-1)%3+1]=k%10;
    35             if(k%10==0)px=(i-1)/3+1,py=(i-1)%3+1;
    36             k/=10;
    37         }
    38         for(int i=0;i<4;i++){
    39             int dx=px+dr[i][0],dy=py+dr[i][1];
    40             if(dx<1||dy<1||dx>3||dy>3)continue;
    41             swap(c[dx][dy],c[px][py]);
    42             int val=rebiu();
    43             if(v[val]==v[x]){
    44                 swap(c[dx][dy],c[px][py]);
    45                 continue;
    46             }//利用v顺便记忆化一下 
    47             if(v[val]+v[x]==3){
    48                 finl=ans[val]+ans[x];
    49                 flag=1;break;
    50             }//两个不同类型相遇 就是答案 
    51             v[val]=v[x],ans[val]=ans[x]+1;
    52             q.push(val);
    53             swap(c[dx][dy],c[px][py]);
    54         }
    55         if(flag)break;
    56     }
    57 }
    58 
    59 
    60 int main(){
    61     scanf("%d",&n);
    62     bfs(n);
    63     cout<<finl;
    64 }

    A*

    对于一个状态n, 经过n的答案 f* [ n ] = g* [ n ] + h* [ n ].

    g*表示从出发点到n,h*表示从n到终点。

    我们可以设置估价函数 f [ n ] = g [ n ] + h [ n ]

    h是我们对未来的预估,且 h [ n ] <=h* [ n ]

    这样可以保证求出最优解且h越大跑的越快。

    对于一个题有多种估价方式,只要正确,大胆跑就完事了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<map>
     6 using namespace std;
     7 int n;
     8 int brd=0;
     9 int c[4][4];
    10 int finl[4][4]={{0,0,0,0},{0,1,2,3},{0,8,0,4},{0,7,6,5}};
    11 int dr[4][2]={{0,1},{1,0},{-1,0},{0,-1}};//方向需要特殊排列一下 
    12 int px,py;
    13 int flag=0;
    14 int k=1;
    15 inline bool lok(int s){
    16     int dif=0;
    17     for(int i=1;i<=3;i++)
    18         for(int j=1;j<=3;j++)
    19             if(c[i][j]!=finl[i][j]){
    20                 ++dif;
    21                 if(dif+s>k)return false;
    22             }
    23     return true;
    24 }/*估价函数可以更优。目前我知道最优的
    25 应该是每个点与目标位置的曼哈顿距离(不算空格
    26 因为空格位置在点移动过程中就改变了,不用另算) 
    27 */ 
    28 inline bool check(){
    29     for(int i=1;i<=3;i++)
    30         for(int j=1;j<=3;j++)
    31         if(c[i][j]!=finl[i][j])return false;
    32     return true;
    33 }
    34 
    35 inline void dfs(int x,int y,int s,int pre){
    36     if(s==k){
    37         if(check())flag=1;
    38         return;
    39     }
    40     if(flag)return;
    41     for(int i=0;i<4;i++){
    42         int dx=x+dr[i][0],dy=y+dr[i][1];
    43         if(dx<1||dx>3||dy<1||dy>3||i+pre==3)continue;//i+pre是为了防止搜回去,mp存状态T了 
    44         swap(c[dx][dy],c[x][y]);
    45         if(lok(s))
    46             dfs(dx,dy,s+1,i);
    47         swap(c[dx][dy],c[x][y]);
    48     }
    49 }
    50 
    51 int main(){
    52     scanf("%d",&n);
    53     if(n==123804765){
    54         printf("0");return 0;
    55     }
    56     for(int i=8;i>=0;i--){
    57         c[i/3+1][(i%3)+1]=n%10;
    58         if(n%10==0)px=i/3+1,py=(i%3)+1;
    59         n/=10;
    60     }
    61     while(k++){
    62         dfs(px,py,0,-1);
    63         if(flag){
    64             printf("%d",k);
    65             return 0;
    66         }
    67     }
    68 }

    IDA*

    在搜索深度不确定,目标状态不明确(有多解)时使用IDA* 。

    ID==迭代加深。

  • 相关阅读:
    OpenJDK源码研究笔记(十二):JDBC中的元数据,数据库元数据(DatabaseMetaData),参数元数据(ParameterMetaData),结果集元数据(ResultSetMetaDa
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
  • 原文地址:https://www.cnblogs.com/chiyo/p/11182186.html
Copyright © 2020-2023  润新知