【UKIEPC2015 J】【趣味迷宫搜索】Jelly Raid 来回巡逻 不被发觉 循环节预处理

#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=0,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
int casenum,casei;
char a[64][64];
bool e[120][64][64];//用e[u][i][j]表示在时刻为u时,位置(i,j)的安全性
int n,m,p,g;
int sty,stx,edy,edx;
int h,t;
struct B{int y,x;}b[205][120];//用b[i][j]表示第i个保安在循环节定位为j时的位置
const int dy[4]={-1,0,0,1};
const int dx[4]={0,-1,1,0};
const int L=3600*125;
int qu[L],qy[L],qx[L];
void dfs(int u,int y,int x)
{
    e[u][y][x]=0;
    for(int i=0;i<4;i++)
    {
        int yy=y;
        int xx=x;
        while(1)
        {
            yy+=dy[i];
            xx+=dx[i];
            if(a[yy][xx]!='.')break;
            e[u][yy][xx]=0;
        }
    }
}
bool inq(int u,int y,int x)
{
	int uu=u%120;
    if(y==edy&&x==edx)return 1;
    if(!e[uu][y][x])return 0;
	e[uu][y][x]=0;
    qu[t]=u;
    qy[t]=y;
    qx[t++]=x;
    return 0;
}
int bfs()
{
    h=t=0;
    inq(0,sty,stx);
    while(h<t)
    {
        int u=qu[h]+1;
        int y=qy[h];
        int x=qx[h++];
        for(int i=0;i<4;i++)
        {
            if(inq(u,y+dy[i],x+dx[i]))return u;
        }
        if(inq(u,y,x))return u;
    }
    return -1;
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
        MS(a,0);
        MS(e,0);
        //scanf(" (%d%d)",&sty,&stx);
        //scanf(" (%d%d)",&edy,&edx);
		scanf("%*[^0-9]%d%d)",&sty,&stx);
		scanf("%*[^0-9]%d%d)",&edy,&edx);
        for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
        for(int u=0;u<120;u++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    e[u][i][j]=a[i][j]=='.';
                }
            }
        }
        scanf("%d",&p);
        for(int i=1;i<=p;i++)
        {
            scanf("%d",&g);
            for(int j=0;j<g;j++)scanf(" (%d%d)",&b[i][j].y,&b[i][j].x);
            int G=g+g-2;
            for(int j=g;j<G;j++)b[i][j]=b[i][G-j];
            if(G==0)for(int j=1;j<120;j++)b[i][j]=b[i][0];
            else for(int j=G;j<120;j++)b[i][j]=b[i][j%G];
        }
        for(int i=0;i<120;i++)
        {
            for(int j=1;j<=p;j++)
            {
                int y=b[j][i].y;
                int x=b[j][i].x;
                dfs(i,y,x);
            }
        }
        int ans=bfs();
        if(ans==-1)puts("IMPOSSIBLE");
        else printf("%d\n",ans);
	}
	return 0;
}
/*
【trick&&吐槽】
1,scanf可以实现很多神奇的功能。
比如:scanf(" %c")可以先吃掉所有换行和空格,然后读入第一个可见字符
比如:scanf("(%d,%d")可以实现读入类似于"(1,2)"这样格式的数
这道题比赛时没能实现第二个功能,是因为'('之前有空格,我们只要改成scanf(" (%d%d)",&y,&x))就可以了
再不要忘记了——
比如:scanf("%[0-9])就是只读入数字,遇到不是数字终止
比如:scanf("%[^0-9]就是只读入非数字,遇到数字终止
即这题也可以写成scanf("%*[^0-9]%d%d)",&sty,&stx);

2,除法一定要提防除0

【题意】
给你一个n(60)*m(60)的迷宫,格子是'.'表示为可行,是'#'表示为障碍物。
我们一开始在(sty,stx),最后想要到达(edy,edx)。
每次可以上下左右走一步,或者呆在原地静止不动。
然而地图上有p([1,200])个人在巡逻,每个人巡逻的路线是一条长度为[1,g]的相邻格点段,一旦走到格点段的端点1或g,就往回走。
如果在同一时刻,我们与任何一个巡逻者位于同一行或同一列,且两者之间没有任何障碍物,那么我们就会被逮到,就失败。
特别的一点是,如果我们在刚刚到达edy,edx时刻被抓到,那么也同样认定为successful

现在问你,最少的使得我们到达(edy,edx)的步数。
如果无法successful,输出-1

【类型】
迷宫搜索

【分析】
首先这道题,因为巡逻的路线长度为g=[1,7],所以我们可以把其映射得到一整个循环长度G=g*2-2,
也就是说,每经过G步,某个巡逻者的状态是相同的。
巡逻长度[1,7]所对应的巡逻长度,分别是[0,2,4,6,8,10,12]
这些数的最小公倍数是120,也就是说,每过120步,所有保安的位置都与之前对应相同。

于是,我们一开始可以做预处理。
得到,当现在的时刻为t时的所有保安的位置,并进而得到地图上每个点是否安全。
然后,这个人就可以直接做三位状态的bfs(步数,纵坐标,横坐标)。
然后第一次达到的(edy,edx)的步数就是最后的答案。

还有一个细节,就是我们可以在到达(edy,edx)的同时被抓获。
这个可以通过调整入队的判定次序来实现。
1st,是否之前走过,走过就return
2nd,是否到达终点,到达就yes
3rd,是否安全,不安全就return

这道题就这么做完了

【时间复杂度&&优化】
预处理的复杂度是O(p*120*60),也不过是1.44e6
bfs的复杂度是O(n*m*120),也不过是3600*120不超过5e5。
所以这题可以顺利AC

【数据】
input
5 5
(2 5) (5 3)
.....
.#.#.
.#.#.
....#
.#.##
1
6 (4 2) (4 3) (3 3) (2 3) (1 3) (1 2)
output
26

input
5 4
(1 4) (5 4)
....
..#.
###.
....
###.
2
2 (2 2) (2 1)
4 (4 1) (4 2) (4 3) (4 3)
output
-1

input(小心除零)
5 5
(2 5) (5 3)
.....
.#.#.
.#.#.
....#
.#.##
1
1 (3 1)
output

*/

你可能感兴趣的:(算法,搜索,ACM,ICPC)