【bzoj 2541】 [Ctsc2000]冰原探险(BFS)

59 篇文章 0 订阅
35 篇文章 0 订阅

2541: [Ctsc2000]冰原探险

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 39  Solved: 25
[Submit][Status][Discuss]

Description

传说中,南极有一片广阔的冰原,在冰原下藏有史前文明的遗址。整个冰原被横竖划分成了很多个大小相等的方格。在这个冰原上有N个大小不等的矩形冰山,这些巨大的冰山有着和南极一样古老的历史,每个矩形冰山至少占据一个方格,且其必定完整地占据方格。冰山和冰山之间不会重叠,也不会有边或点相连。以下两种情况均是不可能出现的:

ACM探险队在经过多年准备之后决定在这个冰原上寻找遗址。根据他们掌握的资料,在这个冰原上一个大小为一格的深洞中,藏有一个由史前人类制作的开关。而唯一可以打开这个开关的是一个占据接近一格的可移动的小冰块。显然,在南极是不可能有这样小的独立冰块的,所以这块冰块也一定是史前文明的产物。他们在想办法把这个冰块推到洞里去,这样就可以打开一条通往冰原底部的通道,发掘史前文明的秘密。冰块的起始位置与深洞的位置均不和任何冰山相邻。这个冰原上的冰面和冰山都是完全光滑的,轻轻的推动冰块就可以使这个冰块向前滑行,直到撞到一座冰山就在它的边上停下来。冰块可以穿过冰面上所有没有冰山的区域,也可以从两座冰山之间穿过(见下图)。冰块只能沿网格方向推动。

请你帮助他们以最少的推动次数将冰块推入深洞中。

Input

输入文件第一行为冰山的个数N (1<=N<=4000),第二行为冰块开始所在的方格坐标X1,Y1,第三行为深洞所在的方格坐标X2,Y2,以下N行每行有四个数,分别是每个冰山所占的格子左上角和右下角坐标Xi1,Yi1,Xi2,Yi2

 

Output

输出文件仅包含一个整数,为最少推动冰块的次数。如果无法将冰块推入深洞中,则输出0。

Sample Input

2
1 1
5 5
1 3 3 3
6 2 8

Sample Output

HINT

Source

[Submit][Status][Discuss]

【题解】【BFS】

【算法简单,但乍一看没什么思路。就算是想到BFS也是很暴力的直接BFS】

【实际这道题,因为整个网格没有边界,所以不能直接暴力。由于每一次推小冰块只有在打到冰山才停止。我们可以预处理若冰块打到当前冰山的当前面,下一次可能再打到哪一冰山的哪一面。】

【so,我们把每一冰山的每一面作为一个点,由当前点向可能转移到的点连有向边,并把最开始的小冰块和深洞也作为一个点】

【预处理后,跑BFS最短路】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
struct node{
	ll up,down,left,right;
	int num[5];
}ice[4010];
queue<int>que;
int a[200010],nxt[200010],p[200010],tot;
ll xi,yi,xt,yt;
int n,cnt,dis[200010];
bool vis[200010];
int tmp(node a,node b)
{
	if(a.down<b.down) return 1;
	if(a.down>b.down) return 0;
	if(a.left<b.left) return 1;
	return 0;
}
inline void add(int x,int y)
{
	tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
}
inline int find_up(ll x,ll y,int opt)
{
	int ans=-1,low=-1;
	for(int i=1;i<=n;++i)
	 if(i!=opt)
	  if(ice[i].left<=x&&ice[i].right>=x&&ice[i].down>=y)
	   if(low==-1||low>ice[i].down) low=ice[i].down,ans=ice[i].num[1];
	if(x==xt&&y<=yt&&(low>=yt||low==-1)) return cnt;
	return ans;
}
inline int find_down(ll x,ll y,int opt)
{
	int ans=-1,high=-1;
	for(int i=1;i<=n;++i)
	 if(opt!=i)
	  if(ice[i].left<=x&&ice[i].right>=x&&ice[i].up<=y)
	   if(high==-1||high<ice[i].up) high=ice[i].up,ans=ice[i].num[2];
	if(x==xt&&y>=yt&&(high<=yt||high==-1)) return cnt;
	return ans;
}
inline int find_left(ll x,ll y,int opt)
{
	int ans=-1,lmax=-1;
	for(int i=1;i<=n;++i)
	 if(opt!=i)
	  if(ice[i].up>=y&&ice[i].down<=y&&ice[i].right<=x)
	   if(lmax==-1||lmax<ice[i].right) lmax=ice[i].right,ans=ice[i].num[4];
	if(y==yt&&x>=xt&&(lmax<=xt||lmax==-1)) return cnt;
	return ans;
}
inline int find_right(ll x,ll y,int opt)
{
	int ans=-1,rmin=-1;
	for(int i=1;i<=n;++i)
	 if(opt!=i)
	  if(ice[i].up>=y&&ice[i].down<=y&&ice[i].left>=x)
	   if(rmin==-1||rmin>ice[i].left) rmin=ice[i].left,ans=ice[i].num[3];
	if(y==yt&&x<=xt&&(rmin>=xt||rmin==-1)) return cnt;
	return ans;
}
inline void bfs()
{
	int inf;
	memset(dis,127/3,sizeof(dis));
	que.push(1); vis[1]=1; dis[1]=0; inf=dis[0];
	while(!que.empty())
	 {
	 	int u=que.front(); que.pop();
	 	if(u==cnt) break;
	 	for(int i=p[u];i!=-1;i=nxt[i])
	 	 if(dis[a[i]]>dis[u]+1)
	 	  {
	 	  	dis[a[i]]=dis[u]+1;
	 	  	if(!vis[a[i]]) que.push(a[i]),vis[a[i]]=1;
		   }
	 }
    if(dis[cnt]==inf) printf("0\n");
     else printf("%d\n",dis[cnt]);
	return;	
}
int main()
{
//	freopen("ice.in","r",stdin);
//	freopen("ice.out","w",stdout);
	int i,j;
	memset(p,-1,sizeof(p));
	memset(nxt,-1,sizeof(nxt));
	scanf("%d",&n);
	scanf("%lld%lld%lld%lld\n",&xi,&yi,&xt,&yt);
	for(i=1;i<=n;++i) scanf("%lld%lld%lld%lld",&ice[i].left,&ice[i].down,&ice[i].right,&ice[i].up);
	sort(ice+1,ice+n+1,tmp);cnt=1;
	for(i=1;i<=n;++i)
	 for(j=1;j<=4;++j)
	  ice[i].num[j]=++cnt;
	int nm; cnt++;
 	nm=find_up(xi,yi,0); if(nm!=-1) add(1,nm);
	nm=find_down(xi,yi,0); if(nm!=-1) add(1,nm);
	nm=find_left(xi,yi,0); if(nm!=-1) add(1,nm);
	nm=find_right(xi,yi,0); if(nm!=-1) add(1,nm); 
	for(i=1;i<=n;++i)
	 {
	 	int l=ice[i].left,r=ice[i].right,u=ice[i].up,d=ice[i].down;
	 	nm=find_up(l-1,d,i); if(nm!=-1) add(ice[i].num[3],nm);
	 	nm=find_up(r+1,d,i); if(nm!=-1) add(ice[i].num[4],nm);
	 	nm=find_down(l-1,u,i); if(nm!=-1) add(ice[i].num[3],nm);
	 	nm=find_down(r+1,u,i); if(nm!=-1) add(ice[i].num[4],nm);
	 	nm=find_left(r,u+1,i); if(nm!=-1) add(ice[i].num[2],nm);
	 	nm=find_left(r,d-1,i); if(nm!=-1) add(ice[i].num[1],nm);
	 	nm=find_right(l,u+1,i); if(nm!=-1) add(ice[i].num[2],nm);
	 	nm=find_right(l,d-1,i); if(nm!=-1) add(ice[i].num[1],nm);
	 }
	bfs();
	return 0;
}
[大模拟练码力...]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值