题目描述
题解
自己YY了一个非常傻逼的方法,做完之后看网上的人都用了一个非常高级的“射线法”感觉好厉害啊。
不过其实我的方法也和射线法也有相似之处。射线法不过是从中间的森林里连出一条线然后规定线某一边的可以越过线到另一边,而另一边的不能越过线。或者做两次bfs一次从左边穿过一次从右边穿过。而我是以森林的中心画了一个十字,十字的四边不经过森林的地方赋成了4个值,只有收集到全部的4个值的方案才是合法的方案,直接上spfa就行了。
不过我还是感觉这样的做法有可能有不科学的地方。因为这种算法最大的bug在于,有可能它走过去收集到了不足4个值,再原路返回收集其它的,比转一圈的步数要少,那么这种方案也是不合法的。所以也许会有一些潜在的隐患,比如说行和列的奇偶性讨论等等。不过尝试把自己的代码给卡掉结果没有成功。由于是在中心画了一个十字,那么按理来说是不会出现上面说的情况的。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define N 55
int n,m,sx,sy,mid,mnx,mny,mxx,mxy;
char a[N][N],s[N];
int dis[N][N][20],val[N][N];
bool vis[N][N][20];
struct hp{int x,y,has;};
queue <hp> q;
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
void spfa()
{
memset(dis,127,sizeof(dis));dis[sx][sy][0]=0;
vis[sx][sy][0]=true;q.push((hp){sx,sy,0});
while (!q.empty())
{
hp now=q.front();q.pop();
vis[now.x][now.y][now.has]=false;
for (int i=0;i<8;++i)
{
int x=now.x+dx[i],y=now.y+dy[i];
if (x<1||x>n||y<1||y>m||a[x][y]=='X') continue;
if (dis[x][y][(now.has|val[x][y])]>dis[now.x][now.y][now.has]+1)
{
dis[x][y][(now.has|val[x][y])]=dis[now.x][now.y][now.has]+1;
if (!vis[x][y][(now.has|val[x][y])])
{
vis[x][y][(now.has|val[x][y])]=true;
q.push((hp){x,y,(now.has|val[x][y])});
}
}
}
}
}
int main()
{
scanf("%d%d\n",&n,&m);
mnx=n,mny=n,mxx=1,mxy=1;
for (int i=1;i<=n;++i)
{
gets(s);
for (int j=1;j<=m;++j)
{
a[i][j]=s[j-1];
if (s[j-1]=='*') sx=i,sy=j;
if (s[j-1]=='X')
{
mnx=min(mnx,i);mxx=max(mxx,i);
mny=min(mny,j);mxy=max(mny,j);
}
}
}
mid=(mnx+mxx)>>1;
for (int i=1;i<=m;++i)
if (a[mid][i]=='.'||a[mid][i]=='*') val[mid][i]=1;
else break;
for (int i=m;i>=1;--i)
if (a[mid][i]=='.'||a[mid][i]=='*') val[mid][i]=2;
else break;
mid=(mny+mxy)>>1;
for (int i=1;i<=n;++i)
if (a[i][mid]=='.'||a[i][mid]=='*') val[i][mid]=4;
else break;
for (int i=n;i>=1;--i)
if (a[i][mid]=='.'||a[i][mid]=='*') val[i][mid]=8;
else break;
spfa();
printf("%d\n",dis[sx][sy][15]);
}