洛谷P4289 [HAOI2008]移动玩具

题目描述

在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。

输入输出格式

输入格式:

前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。

输出格式:

一个整数,所需要的最少移动次数。

分割线

看到这道题,首先想到几个点:

1.图是1和0构成的,所以每种图可以用二进制来表示,每种图二进制代表的数都不相同。

2.图只能用邻接矩阵存,链式前向星不行。

3.四个方向的移动可以用bfs(广度优先搜索)来实现。

4.读入可以字符串读入,再处理成数字。

5.写两个函数,一个用来加密一种图,一个用来解密。

题还是比较水的,想清楚了怎么加密解密就简单了,刚开始想了好久qwq。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<string>
  4 #include<cmath>
  5 #include<iostream>
  6 #include<algorithm>
  7 #include<queue>
  8 using namespace std;
  9 int tx[]={1,-1,0,0};//四联通图 
 10 int ty[]={0,0,1,-1};//四联通图,注意要坐标要一一对应 
 11 int ans; 
 12 int f[4][4],map[4][4],ans1[4][4],ans2,vis[100010],dis[100010];
 13 int dd(int map1[4][4])//加密 
 14 {
 15     int kk=0;
 16     for(int i=0;i<4;i++)
 17     {
 18         for(int j=0;j<4;j++)
 19         {
 20             kk+=map1[i][j]*(1<<(i*4+j));
 21         }
 22     }
 23     return kk;//返回加密之后的数字,代表这种图 
 24 }
 25 void ff(int x)//解密 
 26 {
 27     for(int i=0;i<4;i++)
 28     {
 29         for(int j=0;j<4;j++)
 30         {
 31             f[i][j]=(x>>(i*4+j))&1;//解密后存在f数组里,f就是你要的图 
 32         }
 33     }
 34 }
 35 void bfs(int x)
 36 {
 37     queue<int>q;
 38     q.push(x);
 39     vis[x]=1;//bfs判断是否在队列里的标记,1就是在队列里,0就是不在队列里 
 40     dis[x]=0;//记录移动步数的变量 
 41     while(!q.empty())
 42     {
 43         int now=q.front();//每次从队列里取出头,就是接下来要操作的图(已经加密的) 
 44         q.pop();
 45         if(now==ans2)//判断这个图跟我们最终要的图是不是同一种的 
 46         {
 47             ans=dis[now];//记录答案,因为bfs是一圈一圈遍历的,所以最先找到的一定是最小的 
 48             return ;//退出bfs 
 49         }
 50         ff(now);//解密,解密后的图在f数组里 
 51         for(int i=0;i<4;i++)
 52         {
 53             for(int j=0;j<4;j++)
 54             {
 55                 if(f[i][j]==1)//找到能够移动的点(玩具) 
 56                 {
 57                     for(int k=0;k<=3;k++)//四联通图遍历四个方向 
 58                     {
 59                         int dx=i+tx[k];
 60                         int dy=j+ty[k];
 61                         if(dx>=0&&dy>=0&&dx<=3&&dy<=3&&f[dx][dy]==0)//优先判断边界,判断是否(dx,dy)这个点能走 
 62                         {
 63                             f[dx][dy]=1;//修改 
 64                             f[i][j]=0;//修改 
 65                             int aa=dd(f);//加密 
 66                             if(vis[aa]==0)//判断是否在队列里
 67                             {
 68                                 vis[aa]=1;//不在就加入队列,vis数组标记为1 
 69                                 dis[aa]=dis[now]+1;//记录移动步数 
 70                                 q.push(aa);//加入队列 
 71                             }
 72                             f[dx][dy]=0;//还原,因为我们始终就用一个图,当然也可以再开一个图来存 
 73                             f[i][j]=1;//还原 
 74                         }
 75                     }
 76                 }
 77             }
 78         }
 79     }
 80 }
 81 int main()
 82 {
 83     for(int i=0;i<4;i++)//读入处理 
 84     {
 85         string s;
 86         cin>>s;
 87         for(int j=0;j<s.size();j++)
 88         {
 89             map[i][j]=s[j]-'0';
 90             f[i][j]=map[i][j];
 91         }
 92     }
 93     for(int i=0;i<4;i++)//读入处理 
 94     {
 95         string s;
 96         cin>>s;
 97         for(int j=0;j<s.size();j++)
 98         {
 99             ans1[i][j]=s[j]-'0';
100         }
101     }
102     ans2=dd(ans1);//加密,得到数值ans2,用来判断 
103     int ff=dd(map);//初始的图要加密 
104     bfs(ff);//bfs从初始的图开始 
105     printf("%d\n",ans);//输出答案,程序结束 
106     return 0;
107 }

end.谢谢阅读

posted @ 2018-10-31 13:45  暗燚  阅读(533)  评论(0编辑  收藏  举报