bzoj 1656: [Usaco2006 Jan] The Grove 树木 (bfs+技巧)

1656: [Usaco2006 Jan] The Grove 树木

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 196   Solved: 124
[ Submit][ Status][ Discuss]

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.

牧场里有一片树林,林子里没有坑.
    贝茜很想知道,最少需要多少步能围绕树林走一圈,最后回到起点.她能上下左右走,也能走对角线格子.牧场被分成R行C列(1≤R≤50,1≤C≤50).下面是一张样例的地图,其中“.”表示贝茜可以走的空地,  “X”表示树林,  “*”表示起点.而贝茜走的最近的路已经特别地用“+”表示出来.
 
 
 
 
题目保证,最短的路径一定可以找到.

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).

    第1行输入R和C,接下来R行C列表示一张地图.地图中的符号如题干所述.

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

HINT

Source

[ Submit][ Status][ Discuss]

题解:bfs+技巧
这道题主要的问题就是将整个森林围绕过来,所以可以从树林的四个边界向四个方向引射线,如果绕其一周的话,每个方向的点都会经过一次,所以用f[i][j][sta]表示到(i,j)状态为sta的路径长度。
其实貌似可以更简单,从任意一个点引一条竖直方向的射线,然后第一次bfs从射线左侧穿出,第二次bfs从射线右侧穿出,然后将两过程接到一起。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define pa pair<int,int>
#define N 53
using namespace std;
int n,m,a[N][N],sx,sy,dis[N][N][20],can[N][N][20],vis[N][N],x[N*N],y[N*N],cnt;
int px[10]={0,1,0,-1,1,1,-1,-1},py[10]={1,0,-1,0,-1,1,-1,1};
struct data {
	int x,y,sta;
};
void bfs()
{
	memset(can,0,sizeof(can));
	data a1; a1.x=sx; a1.y=sy; 
	if (vis[sx][sy]!=-1)  a1.sta=(1<<vis[sx][sy]);
	else a1.sta=0;
	queue<data> p; p.push(a1); can[sx][sy][a1.sta]=1;
	dis[sx][sy][a1.sta]=0;
	while (!p.empty()){
		data now=p.front(); p.pop();
		int x=now.x; int y=now.y;
		for (int i=0;i<8;i++) {
			int nowx=x+px[i]; int nowy=y+py[i]; int t;
			 if (vis[nowx][nowy]!=-1) t=now.sta|(1<<vis[nowx][nowy]);
			 else t=now.sta;
			 if (nowx<=0||nowy<=0||nowx>n||nowy>m||a[nowx][nowy]||can[nowx][nowy][t]) continue;
			 dis[nowx][nowy][t]=dis[x][y][now.sta]+1;
			 can[nowx][nowy][t]=1;
			 data a1; a1.x=nowx; a1.y=nowy; a1.sta=t;
			 p.push(a1);
		}
	}
}
int main()
{
	freopen("input.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&m);
	int xmin=N; int ymin=N; int xmax=0; int ymax=0; 
	for (int i=1;i<=n;i++) {
		char s[N]; scanf("%s",s+1);
		for (int j=1;j<=m;j++) {
			if (s[j]=='X') a[i][j]=1,xmin=min(xmin,i),xmax=max(xmax,i),
			ymin=min(ymin,j),ymax=max(ymax,j);
			if (s[j]=='*') sx=i,sy=j;
		}
	}
	memset(vis,-1,sizeof(vis));
	int mid=(ymin+ymax)/2;	
    for (int i=1;i<=n;i++)
     if (!a[i][mid]) vis[i][mid]=0;
     else break;
    for (int i=n;i>=1;i--) 
     if (!a[i][mid]) vis[i][mid]=1;
     else break;
    mid=(xmin+xmax)/2;
    for (int i=1;i<=m;i++)
     if (!a[mid][i]) vis[mid][i]=2;
     else break;
    for (int i=m;i>=1;i--)
     if (!a[mid][i])  vis[mid][i]=3;
     else break;
    /*for (int i=1;i<=n;i++)
     {
     	for (int j=1;j<=m;j++)
     	 cout<<vis[i][j]<<" ";
     	cout<<endl;
	 }*/
    bfs();
    printf("%d\n",dis[sx][sy][15]);
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值