(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;
}