POJ-3182---The Grove (bfs+虚射线)

The Grove
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 941 Accepted: 462

Description

The pasture contains a small, contiguous grove of trees that has no 'holes' in the middle of the it. Bessie wonders: how far is it to walk around that grove and get back to my starting position? She's just sure there is a way to do it by going from her start location to successive locations by walking horizontally, vertically, or diagonally and counting each move as a single step. Just looking at it, she doesn't think you could pass 'through' the grove on a tricky diagonal. Your job is to calculate the minimum number of steps she must take.

Happily, Bessie lives on a simple world where the pasture is represented by a grid with R rows and C columns (1 <= R <= 50, 1 <= C <= 50). Here's a typical example where '.' is pasture (which Bessie may traverse), 'X' is the grove of trees, '*' represents Bessie's start and end position, and '+' marks one shortest path she can walk to circumnavigate the grove (i.e., the answer):

...+...

..+X+..

.+XXX+.

..+XXX+

..+X..+

...+++*
The path shown is not the only possible shortest path; Bessie might have taken a diagonal step from her start position and achieved a similar length solution. Bessie is happy that she's starting 'outside' the grove instead of in a sort of 'harbor' that could complicate finding the best path.

Input

Line 1: Two space-separated integers: R and C

Lines 2..R+1: Line i+1 describes row i with C characters (with no spaces between them).

Output

Line 1: The single line contains a single integer which is the smallest number of steps required to circumnavigate the grove.

Sample Input

6 7
.......
...X...
..XXX..
...XXX.
...X...
......*

Sample Output

13

Source

USACO 2006 January Silver

题意:从起点出发绕着树林走一圈回到起点,求最少的步数,树林中间不会有洞
思路:最短路径第一想法便是bfs,但这题要围着走一圈,显然只用bfs不行,看了别人的博客,都用到射线法但是都没有解释,看代码理解了好半天总算想通了,讲一下我的理解。所谓的射线法也就是假想图中有一条从一树林中一个点发出射线把图分开(图一),红色的的点为假想的那条射线,由第一棵树发射出来,那么从起点到这条射线就只能顺时针后者逆时针走,正好bfs会向这两个方向搜去(图二),我们规定顺时针方向的可以一直搜到线,逆时针的只能搜到射线上面一条线。那么搜索完之后每个点的步数情况如图三(-1表示树林),我们要求的答案便是到射线的步数和到射线上一条线的步数的和的最小值,要保证选的两条线上的两个点要一部可达,另外题目要求回到起点,所以答案加上1.



AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define inf 999999999
using namespace std;
int n,m;
char str[55][55];
int vis[55][55];//记录步数的数组
struct node
{
    int x;
    int y;
};
queue<node> que;
node tmp,q;
int sx,sy,ex,ey;
int next[8][2]={1,0,-1,0,0,1,0,-1,1,1,1,-1,-1,1,-1,-1};//方向数组,8个方向

bool check(int x,int y,int xx,int yy)
{
    if(x<1||x>n||y<1||y>m||str[x][y]=='X')
        return false;
    if(yy<=ey&&xx==ex&&x==xx-1)//逆时针方向的不能走超过射线
        return false;
    if(yy<=ey&&xx==ex-1&&x==ex)//顺时针方向不能走到射线上
        return false;
    return true;
}

void bfs()
{
    while(que.size())
    {
        tmp=que.front();
        que.pop();
        for(int i=0;i<8;i++)
        {
            int tx=tmp.x+next[i][0];
            int ty=tmp.y+next[i][1];
            if(check(tx,ty,tmp.x,tmp.y))
            {
                q.x=tx;
                q.y=ty;
                vis[tx][ty]=vis[tmp.x][tmp.y]+1;
                str[tx][ty]='X';
                que.push(q);
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int flag=1;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str[i]+1);
            for(int j=1;j<=m;j++)
            {
                if(str[i][j]=='X')
                {
                    if(flag)//记录第一个出现的树的坐标
                        ex=i,ey=j,flag=0;
                }
                if(str[i][j]=='*')//记录起点
                    sx=i,sy=j;
            }
        }
        tmp.x=sx;
        tmp.y=sy;
        que.push(tmp);
        str[sx][sy]='X';
        bfs();
        int ans=inf;
        for(int i=1;i<ey;i++)//找到最优解
        {
            ans=min(ans,vis[ex][i]+vis[ex-1][i+1]);
            ans=min(ans,vis[ex][i]+vis[ex-1][i]);
            if(i!=1)ans=min(ans,vis[ex][i]+vis[ex-1][i-1]);
        }
        ans+=1;
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值