华容道【解题报告】

解题报告

1442. [NOIP2013]华容道

★★★   输入文件:PuzzleNOIP2013.in   输出文件:PuzzleNOIP2013.out   简单对比
时间限制:1 s   内存限制:128 MB

【题目描述】

【输入格式】

输入文件改为:PuzzleNOIP2013.in

【输出格式】

输出文件改为:PuzzleNOIP2013.out

【来源】

CCF全国信息学奥林匹克联赛(NOIP2013)复赛Day2

 

 

 

这道题是一道搜索题,部分分不明确,爆搜可以拿到70分,预处理一下ac。

考虑我们应该如何搜索,对于被标记的格子(下记为x),如果他想往下面走,那么必须先让白格子到达他的下面,其他位置也是同理,当白格子到达他的相邻格子时,x与白格子再交换,所需要的总步数是白格子移动的步数+1,而白格子如何移动呢?我们可以发现,白格子右边的格子往左其实相当于白格子往右,于是我们可以直接视为白格子一步一步移动到指定位置,但需要注意的是,白格子移动过程中不能经过x,于是我们就得到了一个70分的算法,对于初始状态,记录白格子位置,以及x当前的位置,分别向上下左右四个方向bfs,

开一个数组记录到达当前位置最少步数是多少,每次再求出白格子移动所需要的步数,更新数组,一直搜下去即可,类似于spfa吧,但有一个地方需要注意,我们不能单单记录到达xy这个位置时最小步数是多少,而是需要记录到达xy时白格子在i方向时最小步数是多少,不然会出现一种情况:我当前到达xy这个位置步数虽然很少,但我白格子的位置不好,以至于我下一步需要移动很多,导致总步数不是最优。

对于100分,我们可以提前预处理处白格子移动所需要的步数,考虑到x所在位置对白格子移动的影响,并且考虑到时间的问题,我们开一个数组w[i][j][k][o]表示当前位置x在ij,白格子在x的k方向,移动到o方向需要多少步,因为除了初始状态,其他状态白格子总在x格子的旁边4个位置,所以我们对白格子初始位置暴力搜一下搜到x初始位置的周围,然后再搜,白格子移动距离直接调用即可。

但是需要注意一种情况,就是x的初始位置和目标位置是同一个位置,这种情况就直接输出0即可。

贴上代码,因为对着70分改的,所以巨丑无比

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 int n,m,q,a[31][31],b[31][31],dx[4]={0,0,1,-1},dy[4]={1,-1,0,0},w[31][31][4][4];
  4 struct hh{
  5     int x,y,z,step;
  6     hh(){}
  7     hh(int a,int b,int c,int d){
  8         x=a;y=b;z=c;step=d;
  9     }
 10     hh(int a,int b,int c){
 11         x=a;y=b;step=c;
 12     }
 13 };
 14 void bfs(int x,int y,int p,int q,int z){
 15     bool vi[31][31];memset(vi,0,sizeof(vi));
 16     int dis[31][31];memset(dis,-1,sizeof(dis));
 17     queue<hh>S;S.push(hh(x,y,0));
 18     vi[p][q]=1;vi[x][y]=1;
 19     dis[x][y]=0;
 20     while(!S.empty()){
 21         hh u=S.front();S.pop();
 22         for(int i=0;i<4;i++){
 23             int tem1=dx[i]+u.x;
 24             int tem2=dy[i]+u.y;
 25             if(!b[tem1][tem2])continue;
 26             if(!a[tem1][tem2])continue;
 27             if(vi[tem1][tem2])continue;
 28             vi[tem1][tem2]=1;
 29             S.push(hh(tem1,tem2,u.step+1));
 30             dis[tem1][tem2]=u.step+1;
 31         }
 32     }
 33     for(int i=0;i<4;i++){
 34         int tem1=p+dx[i];
 35         int tem2=q+dy[i];
 36         if(!a[tem1][tem2])continue;
 37         if(!b[tem1][tem2])continue;
 38         w[p][q][z][i]=dis[tem1][tem2];
 39     }
 40 }
 41 int get(int q,int w,int e,int r,int x,int y){
 42     queue<hh>S;
 43     bool vi[31][31];memset(vi,0,sizeof(vi));
 44     vi[q][w]=1;vi[x][y]=1;
 45     S.push(hh(q,w,0));
 46     while(!S.empty()){
 47         hh u=S.front();
 48         S.pop();
 49         if(u.x==e&&u.y==r){
 50             return u.step;
 51         }
 52         for(int i=0;i<4;i++){
 53             int tem1=u.x+dx[i];
 54             int tem2=u.y+dy[i];
 55             if(!a[tem1][tem2])continue;
 56             if(!b[tem1][tem2])continue;
 57             if(vi[tem1][tem2])continue;
 58             vi[tem1][tem2]=1;
 59             S.push(hh(tem1,tem2,u.step+1));
 60         }
 61     }return -1;
 62 }
 63 int main()
 64 {
 65     freopen("PuzzleNOIP2013.in","r",stdin);
 66     freopen("PuzzleNOIP2013.out","w",stdout);
 67     scanf("%d%d%d",&n,&m,&q);
 68     for(int i=1;i<=n;i++){
 69         for(int j=1;j<=m;j++){
 70             scanf("%d",&a[i][j]);
 71             b[i][j]=1;
 72         }
 73     }
 74     memset(w,-1,sizeof(w));
 75     for(int i=1;i<=n;i++){
 76         for(int j=1;j<=m;j++){
 77             if(!a[i][j])continue;
 78             for(int k=0;k<4;k++){
 79                 int tem1=dx[k]+i;
 80                 int tem2=dy[k]+j;
 81                 if(!a[tem1][tem2])continue;
 82                 if(!b[tem1][tem2])continue;
 83                 bfs(tem1,tem2,i,j,k);
 84             }
 85         }
 86     }
 87     for(int o=1;o<=q;o++){
 88         int ex,ey,sx,sy,tx,ty,Min=0x3fffffff;
 89         scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
 90         queue<hh>S;int vis[31][31][4];if(sx==tx&&sy==ty){
 91             printf("%d\n",0);
 92             continue;
 93         }
 94         memset(vis,0x3f,sizeof(vis));
 95         for(int i=0;i<4;i++){
 96             int tem1=dx[i]+sx;
 97             int tem2=dy[i]+sy;
 98             if(!a[tem1][tem2])continue;
 99             if(!b[tem1][tem2])continue;
100             int l=get(tem1,tem2,ex,ey,sx,sy);
101             if(l==-1)continue;
102             vis[sx][sy][i]=l;
103             S.push(hh(sx,sy,i,l));
104         }
105         while(!S.empty()){
106             hh u=S.front();
107             S.pop();
108             if(u.x==tx&&u.y==ty){
109                 Min=min(Min,u.step);
110                 continue;
111             }
112             for(int i=0;i<4;i++){
113                 int tem1=dx[i]+u.x;
114                 int tem2=dy[i]+u.y;
115                 if(!b[tem1][tem2])continue;
116                 if(!a[tem1][tem2])continue;
117                 int l=w[u.x][u.y][u.z][i];
118                 if(l==-1)continue;
119                 //cout<<u.z<<" "<<u.x<<" "<<u.y<<" "<<u.step<<endl;
120                 if(vis[tem1][tem2][i^1]<=u.step+1+l)continue;
121                 vis[tem1][tem2][i^1]=u.step+1+l;
122                 S.push(hh(tem1,tem2,i^1,u.step+1+l));
123             }
124         }
125         if(Min!=0x3fffffff)printf("%d\n",Min);
126         else printf("%d\n",-1);
127     }
128     return 0;
129 }
View Code

 

转载于:https://www.cnblogs.com/hyghb/p/7745170.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值