[BZOJ1656][Usaco2006 Jan] The Grove 树木(spfa)

题目描述

传送门

题解

自己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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值