(bfs+二分)2112: Wells的明星生活

很久之前就看到这个题目,今天第一次做出来,纪念一下

2112: Wells的明星生活

csuoj2112

http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2112

Description

和很多人想的不同,Wells其实是很低调的。

刚刚过了520和521,爱信不信,巨怂Wells和喜欢的小姐姐表白了!

Wells那是相当相当低调——之后的约会也是。不过,这次Wells遇到麻烦了……

Wells和小姐姐正在浪漫的约会呢,Wells猛然得到最新消息,有若干狗仔团队正闻讯蜂拥而来(Wells毕竟还是蛮很有名气的嘛)……

Wells真是不理解现在这些人对八卦的渴望怎么那么大,连保密工作做的这么好的Wells都未能幸免,这真是……

算了,别扯了,先溜吧,被狗仔队逮到可不好。

但是,Wells最近心情真的不好,厌睡厌食还厌学,难得和小姐姐的浪漫约会真不想撤,那么就能呆多久呆多久吧,Wells希望在不会被抓的前提下和小姐姐呆尽量久的时间。

城市可以看做是N行N列的方格,每一个格子会是一个建筑物,一片空地,一个狗仔队的窝点,或者是Wells的家。我们认为两个格子如果有相同的边,那么就是相邻的。城市里居民都是良民,每次走的时候,只会走相邻的格子。显然的,在私闯民宅可不好,所以不论是谁都不会闯到建筑物里的……

由于要保护小姐姐, Wells每一秒至多走S步,S可能很大,因为这是爱情的力量!

当Wells得知狗仔队将至的消息时,Wells在和小姐姐约会,而狗仔队都在各自窝点。随着时间的前进,每一秒都会按照如下的顺序发生着一些事件:

  • 如果Wells还在约会地点,那么他可以选择现在这一秒是和小姐姐浪漫,还是开始逃跑——如果是继续浪漫,那么这一秒Wells是不会移动的,如果逃跑,那么接下来Wells可以在城市里移动不超过S步——当然的,开始逃跑了怎么还会有心思浪漫呢?所以一旦离开了,Wells就会保护着小姐姐一直不停的逃跑。当然了,如果在某个位置遇到了狗仔队,那么可怜的Wells就要悲剧了…

  • 当Wells决定或者移动完毕之后,所有的狗仔队会向四周更远的格子移动一步——当然,也只会走到空地上,并且最邪恶的是一旦狗仔队走到了一个格子,就会派一个狗仔留守在那里——或者可以这么说,如果格子a和b是相邻的空地,并且在前一秒狗仔队占据了格子a,那么这一秒过后,a和b都将被狗仔队占据。最开始的时候,狗仔队占据了所有狗仔窝点所在的格子。

狗仔和Wells都不能走出城市。并且,显然的,根据规则,Wells继续和小姐姐浪漫的时间,必然是一个整数秒。

狗仔队是无法占领Wells的家的,所以Wells只要回到家里就安全啦!

现在Wells想知道如果要能够安全带着小姐姐回到家,那么最多他还能和小姐姐浪漫多长时间呢?

Input

第一行两个整数,N,S ,1≤N≤800 1≤S≤1000

接下来N行每行N个字符描述每个格子的具体情况

T 这个格子是一个建筑物

G 这是一块空地

M 这是Wells和小姐姐浪漫的地方,当然也是个空地

D 这是Wells的家

H 这是一个狗仔队的窝点

Output

一行一个整数,表示Wells最多还能和小姐姐浪漫多长时间,如果Wells不可能回到家了,那么就输出-1,否则显然答案是非负整数。

约定:

地图中有且仅有一个M,一个D,并且至少有一个H。

同样的,保证一定存在一条路能够从M走到D(否则Wells是怎么去和小姐姐约会的呢?)

保证答案不为无限大

Sample Input

7 3 
TTTTTTT 
TGGGGGT 
TGGGGGT 
MGGGGGD 
TGGGGGT 
TGGGGGT 
TGHHGGT 

Sample Output

2 

Hint

对于样例,一种可行的方法是,停留两秒,然后第一秒走三步到(3,3)第二秒走三步到(3,6),第三秒走两步到(4,7)中途不会遇到狗仔队 左上角格子为(1,1)

Source

Author

Wells

csuoj上有思路写得很好,这里就直接粘贴过来,不再赘述:

      1是主人公本身在动,每秒可以动S步

      2狗仔每秒同时向4个方向动,同时考虑太过复杂。

      hint其实是在提示,如果留意了,其实可以看出在确定等待若干秒后在去判定能否到家比要容易,因此我们将计算问题转化为判定性问题,由于两个变量步长完全不一样,同时处理是不太方便的,故可以想到预处理出每一个格子被狗仔占领的最早时间tij,即主人公如果想要经过格子,那么时间必须严格小于tij那么主人公的S步怎么解决呢?其实可以像只能走一步一样利用bfs解决,记停顿时间为tim只要记录从开始走的步数stp,下一步走到的时间至少是(stp + 1)/S,记为T(其实贪心的来看,走得快一定比走得慢要优,所以一定是走S步的,除非离家已经不足S步),枚举主人公下一步走的方向,若满足T + tim < tij那么表示这个状态可走,严格小于保证了下一秒狗仔队抓不住主人公,那么现在就只需要一直扩展状态一直到回到家或者无状态可更新(-1)这个时间复杂度是多少?预处理狗仔到达时间,枚举停留时间,然后去bfs判断,由于保证时间不为无限大则为O(n * n * n * n)=O(n4),在n < =800的情况下无法通过,其实可以发现这个问题具有二分性,若一个时间tim能使得主人公到家,那么小于tim同样也可以,若不能,那么大于tim也不能,因此将上一种做法的枚举时间改为二分时间即可,复杂度(log(n * n)*n * n)=O(n2logn),n < =800绰绰有余

对狗仔的预处理代码:

void pre_solve() 
{
	node a;
	while(!q.empty())
	{
		a=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			node b=a;
			b.y+=mve[i][0];
			b.x+=mve[i][1];
			if(judge(b.x,b.y)&&!flag[b.x][b.y])
			{
				++b.tim;
				q.push(b);
				flag[b.x][b.y]=1;
				risk[b.x][b.y]=b;
			}
		}
	}
}

对wells是否能走到的判断:

int result_judge(int t)
{
	memset(flag_man,0,sizeof(flag_man));
	flag_man[begx][begy]=1;
	while(!qt.empty()) qt.pop();
	node m;
	m.x=begx;m.y=begy;
	m.tim=t;m.rest=s;
	qt.push(m);
	while(!qt.empty())
	{
		m=qt.front();
		qt.pop();
		if(m.x==endx&&m.y==endy)
		{
			return 1;
		}
		
		for(int i=0;i<4;i++)
		{
			node p=m;
			p.x+=mve[i][0];
			p.y+=mve[i][1];
			--p.rest;
			if(p.rest==0)
			{
				++p.tim;
				p.rest=s;
			}
			if(judge(p.x,p.y)&&!flag_man[p.x][p.y]&&risk[p.x][p.y].tim>p.tim)
			{
				flag_man[p.x][p.y]=1;
				
				qt.push(p);
			}
		}
	}
	return 0;
}

二分得出答案:

int r=len*len,l=0;
	if(!result_judge(0))
	{
		printf("-1\n");
		return 0;
	}
	while(l<=r)
	{
		int mid=md(l,r);
		if(result_judge(mid))
		{
			l=mid+1;
		}
		else
		{
			r=mid-1;
		}	
		
	}
	cout<<r<<endl;

总代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>


using namespace std;
#define md(x,y) (x+y)/2 
const int INF=0x3f3f3f3f;
struct node{
	int x,y;
	int tim;
	int rest;
	friend bool operator<(node a,node b)
	{
		return a.tim>b.tim;
	}
};
queue<node> q;
const int N=805;
char map[N][N];//竖的是x横的是y 
int flag[N][N];
node risk[N][N];
int mve[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int len,s;
inline int judge(int x,int y)
{
	if(x>=1&&x<=len&&y>=1&&y<=len&&map[x][y]!='T') return 1;
	else return 0;
}
int begx,begy;
int endx,endy;
void read_map()
{
	int i,j;
	for(i=1;i<=len;i++)
	{
		for(j=1;j<=len;j++)
		{
			switch(map[i][j])
			{
				case 'T':
					risk[i][j].tim=INF;
					break;
				case 'G':break;
				case 'M':
					risk[i][j].tim=INF;
					begx=i;begy=j;
					break;
				case 'D':
					endx=i;endy=j;
					break;
				case 'H':
					node h;
					h.x=i;h.y=j;h.tim=0;
					risk[i][j]=h;
					flag[i][j]=1;
					q.push(h);
					break;
			}
		}
	}
}
queue<node> qt;
int flag_man[N][N];
int result_judge(int t)
{
	memset(flag_man,0,sizeof(flag_man));
	flag_man[begx][begy]=1;
	while(!qt.empty()) qt.pop();
	node m;
	m.x=begx;m.y=begy;
	m.tim=t;m.rest=s;
	qt.push(m);
	while(!qt.empty())
	{
		m=qt.front();
		qt.pop();
		if(m.x==endx&&m.y==endy)
		{
			return 1;
		}
		
		for(int i=0;i<4;i++)
		{
			node p=m;
			p.x+=mve[i][0];
			p.y+=mve[i][1];
			--p.rest;
			if(p.rest==0)
			{
				++p.tim;
				p.rest=s;
			}
			if(judge(p.x,p.y)&&!flag_man[p.x][p.y]&&risk[p.x][p.y].tim>p.tim)
			{
				flag_man[p.x][p.y]=1;
				
				qt.push(p);
			}
		}
	}
	return 0;
}
void pre_solve() 
{
	node a;
	while(!q.empty())
	{
		a=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			node b=a;
			b.y+=mve[i][0];
			b.x+=mve[i][1];
			if(judge(b.x,b.y)&&!flag[b.x][b.y])
			{
				++b.tim;
				q.push(b);
				flag[b.x][b.y]=1;
				risk[b.x][b.y]=b;
			}
		}
	}
}
int main()
{
	memset(flag,0,sizeof(flag));
	cin>>len>>s;
	int i;
	for(i=1;i<=len;i++)
	{
		scanf("%s",map[i]+1);
	}
	read_map();
	pre_solve();
	int r=len*len,l=0;
	if(!result_judge(0))
	{
		printf("-1\n");
		return 0;
	}
	while(l<=r)
	{
		int mid=md(l,r);
		if(result_judge(mid))
		{
			l=mid+1;
		}
		else
		{
			r=mid-1;
		}	
		
	}
	cout<<r<<endl;
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值