【HDU - 3533】Escape(bfs)

Descriptions:

一个人从(0,0)跑到(n,m),只有k点能量,一秒消耗一点,在图中有k个炮塔,给出炮塔的射击方向c,射击间隔t,子弹速度v,坐标x,y
问这个人能不能安全到达终点
要求:
1.人不能到达炮塔所在的坐标
2.炮塔会挡住子弹
3.途中遇到子弹是安全的,但是人如果停在这个坐标,而子弹也刚好到这个坐标,人就被射死
4.人可以选择停止不动

Input

对于每个测试用例,第一行有四个整数,m、n、k和d (2<=m, n<=100, 0<=k<=100, m+ n<=d<=1000)。m和n是战场的大小,k是城堡的数量,d是A最初拥有的能量单位。接下来的k行分别描述了这些城堡。每一行包含一个字符c和四个整数,t, v, x和y。c是“N”,“S”,“E”或“W”给城堡的发射方向,t, v是子弹的速度(即单位通过每秒),和(x, y)是城堡的位置。这里我们假设,如果一座城堡被其他城堡击中,它会挡住其他人的射击,但不会被摧毁。两颗子弹会在不影响它们的方向和速度的情况下擦肩而过。

当小A开始逃跑时,所有的城堡都开始射击。

继续到文件末尾。

Output

如果小A能逃跑,在一行中输入其所需的最小秒数。否则,在一行中输出"Bad luck!"(无需带引号)

Sample Input

4 4 3 10
N 1 1 1 1
W 1 1 3 2
W 2 1 2 4
4 4 3 10
N 1 1 1 1
W 1 1 3 2
W 1 1 2 4

Sample Output

9
Bad luck!

坑点:炮台的位置是不能走的,不只是会0距离射击,而是那个位置根本不能去,题目没有表述清楚这一点。

其实不难,我们只需要看当人位于某个点的时候,其四个方向是否有炮塔,这个炮塔是都向人的方向射击,然后再看子弹是否刚好位于这个坐标即可。

而标记的话,book[x][y][time],对于time时刻,人位于x,y的情况只需要访问一次,这是唯一的

#include<bits/stdc++.h>
using namespace std;
const int dx[] = { 1,-1,0,0,0 };
const int dy[] = { 0,0,1,-1,0 };
int n, m, k, hp;
int map[105][105];
bool book[105][105][105];

struct period {
    char c; int t, v;
}s[105][105];

struct node {
    int x, y, step;
};

#define Check(x,y) ( x < 0 || x > n || y < 0 || y > m )

void bfs()
{
    node now, net;
    queue<node> q;
    int i, j, flag, dis, timee;
    now.x = now.y = now.step = 0;
    q.push(now);
    book[0][0][0] = true;
    while (!q.empty())
    {
        now = q.front();
        q.pop();
        if (now.step > hp)
            break;
        if (now.x == n && now.y == m)
        {
            cout << now.step << endl;
            return;
        }
        for (i = 0; i < 5; i++)
        {
            net = now;
            net.x += dx[i];
            net.y += dy[i];
            net.step++;
            if (Check(net.x, net.y))
                continue;
            if (!s[net.x][net.y].t && !book[net.x][net.y][net.step] && net.step <= hp)//在符合条件的情况下,枚举四个方向
            {
                flag = 1;
                for (j = net.x - 1; j >= 0; j--)//当位于这点,我们往北向寻找是否有朝南方向射击的炮台
                {
                    if (s[j][net.y].t && s[j][net.y].c == 'S')//找到第一个炮台,且这个炮台是朝南射击的
                    {
                        dis = net.x - j;//看炮台与人的距离
                        if (dis%s[j][net.y].v)
                            break;//因为不需要看子弹中途的点,子弹每一秒跑v,距离是dis,dis不能整除v的话,那么子弹是不可能停在这个点的
                        timee = net.step - dis / s[j][net.y].v;//人走的时间减去第一个子弹飞行到这个位置所需的时间
                        if (timee < 0)
                            break;//为负数就是第一个子弹都没有经过这个点,那么人绝对安全
                        if (timee%s[j][net.y].t == 0)//看间隔,能整除,那么就是后续有子弹刚好到这个点,人死定了
                        {
                            flag = 0;
                            break;
                        }
                    }
                    if (s[j][net.y].t)//找到炮台但不是朝南射击,那么这个炮台会当下后面所有子弹,所以北方向安全我们不需要再找
                        break;
                }
                if (!flag)//这个方向都死定了,后面也就不需要看了
                    continue;
                //其他方向也是一样的道理,就不注释了
                for (j = net.x + 1; j <= n; j++)
                {
                    if (s[j][net.y].t && s[j][net.y].c == 'N')
                    {
                        dis = j - net.x;
                        if (dis%s[j][net.y].v)
                            break;
                        timee = net.step - dis / s[j][net.y].v;
                        if (timee < 0)
                            break;
                        if (timee%s[j][net.y].t == 0)
                        {
                            flag = 0;
                            break;
                        }
                    }
                    if (s[j][net.y].t)
                        break;
                }
                if (!flag)
                    continue;
                for (j = net.y - 1; j >= 0; j--)
                {
                    if (s[net.x][j].t && s[net.x][j].c == 'E')
                    {
                        dis = net.y - j;
                        if (dis%s[net.x][j].v)
                            break;
                        timee = net.step - dis / s[net.x][j].v;
                        if (timee < 0)
                            break;
                        if (timee%s[net.x][j].t == 0)
                        {
                            flag = 0;
                            break;
                        }
                    }
                    if (s[net.x][j].t)
                        break;
                }
                if (!flag)
                    continue;
                for (j = net.y + 1; j <= m; j++)
                {
                    if (s[net.x][j].t && s[net.x][j].c == 'W')
                    {
                        dis = j - net.y;
                        if (dis%s[net.x][j].v)
                            break;
                        timee = net.step - dis / s[net.x][j].v;
                        if (timee < 0)
                            break;
                        if (timee%s[net.x][j].t == 0)
                        {
                            flag = 0;
                            break;
                        }
                    }
                    if (s[net.x][j].t)
                        break;
                }
                if (!flag)
                    continue;
                book[net.x][net.y][net.step] = true;
                q.push(net);
            }
        }
    }
    cout << "Bad luck!" << endl;
}

int main() {
    //freopen("in.txt", "r", stdin);
    while (cin >> n >> m >> k >> hp) {
        memset(book, false ,sizeof book);
        memset(s, false ,sizeof s);
        for (int i = 0; i < k; ++i) {
            char c; int t, v, x, y;
            cin >> c >> t >> v >> x >> y;
            s[x][y].c = c;
            s[x][y].t = t;
            s[x][y].v = v;
        }
        bfs();
    }
    return 0;
}

Astar解法( A*算法每次从 优先队列 中取出一个 最小的,然后更新相邻的状态。)

用A*很合适,h估计距离可以直接用曼哈顿距离,并且保证一定比实际距离小,能保证寻到最优解。注意A*算法能找到最优解的充要条件是估计距离h小于等于实际距离,否则只能找到路径,但不一定是最短路,只能称作A算法。本题中,由于曼哈顿距离是当前位置到终点的最短路径,而实际情况只可能比这个路径长,因此一定可以找到最短距离解。

路线上是否有子弹的计算:寻找四周指向当前位置的炮台(并且注意到一个炮台能挡住后面的子弹),计算到达当前位置的t0时间,即距离\(dist/v\),然后接下来就是周期t重复了,则看当前时间\(tt==t0+t*k\)是否成立,即\((tt-t0)%t\)是否为0。

#include<bits/stdc++.h>
using namespace std;

int m, n, k, d;

struct castle
{
    char c;
    int t, v;
} mp[105][105];

bool vis[105][105][1005];

void init()
{
    memset(vis, false, sizeof vis);
    for (int i = 0; i <= m; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            mp[i][j].t = 0;
            mp[i][j].v = 0;
        }
    }
}

struct node
{
    int g, h, x, y;
    bool operator<(const node n1)const
    {
        return (n1.g + n1.h) < (g + h);
    }
};


int get_h(int x, int y)
{
    return abs(x - m) + abs(y - n);
}

int dx[5] = { 0,0,-1,1,0 };
int dy[5] = { -1,1,0,0,0 };

int astar()
{
    if (mp[0][0].t != 0) return -1;
    node e;
    e.g = 0;
    e.h = get_h(0, 0);
    e.x = 0;
    e.y = 0;

    priority_queue<node> pq;
    pq.push(e);
    vis[0][0][0] = true;

    while (!pq.empty())
    {
        node cur = pq.top();
        pq.pop();
        int x = cur.x;
        int y = cur.y;

        for (int i = 0; i < 5; i++)
        {
            int xx = x + dx[i];
            int yy = y + dy[i];
            if (xx<0 || xx>m || yy<0 || yy>n) continue;

            bool flag = false;

            node next = cur;
            next.g++;
            if (vis[xx][yy][next.g] == true) continue;
            if (mp[xx][yy].t) continue;
            if (xx == m && yy == n) return next.g;

            for (int cx = xx - 1; cx >= 0; cx--)
            {
                if (mp[cx][yy].t != 0)
                {
                    int L = xx - cx;
                    if (mp[cx][yy].c == 'S'&&L%mp[cx][yy].v == 0)
                    {
                        int t0 = L / mp[cx][yy].v;
                        int dt = next.g - t0;
                        if (dt >= 0 && (dt%mp[cx][yy].t == 0)) flag = true;
                    }
                    break;
                }
            }

            for (int cx = xx + 1; cx <= m; cx++)
            {
                if (mp[cx][yy].t != 0)
                {
                    int L = cx - xx;
                    if (mp[cx][yy].c == 'N'&&L%mp[cx][yy].v == 0)
                    {
                        int t0 = L / mp[cx][yy].v;
                        int dt = next.g - t0;
                        if (dt >= 0 && (dt%mp[cx][yy].t == 0)) flag = true;
                    }
                    break;
                }
            }

            for (int cy = yy - 1; cy >= 0; cy--)
            {
                if (mp[xx][cy].t != 0)
                {
                    int L = yy - cy;
                    if (mp[xx][cy].c == 'E'&&L%mp[xx][cy].v == 0)
                    {
                        int t0 = L / mp[xx][cy].v;
                        int dt = next.g - t0;
                        if (dt >= 0 && (dt%mp[xx][cy].t == 0)) flag = true;
                    }
                    break;
                }
            }

            for (int cy = yy + 1; cy <= n; cy++)
            {
                if (mp[xx][cy].t != 0)
                {
                    int L = cy - yy;
                    if (mp[xx][cy].c == 'W'&&L%mp[xx][cy].v == 0)
                    {
                        int t0 = L / mp[xx][cy].v;
                        int dt = next.g - t0;
                        if (dt >= 0 && (dt%mp[xx][cy].t == 0)) flag = true;
                    }
                    break;
                }
            }

            if (flag) continue;
            next.x = xx;
            next.y = yy;
            next.h = get_h(xx, yy);
            if ((next.g + next.h) > d) continue;
            pq.push(next);
            vis[xx][yy][next.g] = true;
        }
    }

    return -1;
}

int main()
{
    freopen("in.txt", "r", stdin);
    while (scanf("%d%d%d%d", &m, &n, &k, &d) == 4)
    {
        init();
        char c;
        int t, v, x, y;
        getchar();
        for (int kk = 0; kk < k; kk++)
        {
            c = getchar();
            scanf("%d%d%d%d", &t, &v, &x, &y);
            getchar();
            mp[x][y].c = c;
            mp[x][y].t = t;
            mp[x][y].v = v;
        }

        int ret = astar();

        if (ret == -1) printf("Bad luck!\n");
        else printf("%d\n", ret);
    }

    return 0;
}

Time 从第一种方法的1762ms 降到了 234ms

bfs + 预处理 + 剪枝

#include<bits/stdc++.h>
using namespace std;
const int maxt=1000+10;
const int maxn2=100+5;
struct castle{       //碉堡节点 
	int t,v,x,y;     //时间间隔、速度、坐标 
	char c;
	castle(int t=0,int v=0,int x=0,int y=0,char c='N'):t(t),v(v),x(x),y(y),c(c){}
}cas[maxn2];
struct node{
	int x,y;
	int time;   //走到当前节点花了多少时间 
	node(int x=0,int y=0,int time=0):x(x),y(y),time(time){}
};
bool vis[maxt][maxn2][maxn2];  
bool remb[maxt][maxn2][maxn2]; //记录每个时刻那些位置有炮弹 
bool g[maxn2][maxn2];   //记录碉堡的坐标 
int m,n,k,d;
int dir[][2]={{1,0},{-1,0},{0,-1},{0,1},{0,0}};
bool isValid(node &nd){
	return nd.x>=0&&nd.x<=m && nd.y>=0&&nd.y<=n;
}
void solve(){
	memset(remb,false,sizeof(remb));	
	for(int i=0;i<k;i++){
		int x=0,y=0;
		switch(cas[i].c){
			case 'N': x=-1;break;
			case 'S': x=1;break;
			case 'W': y=-1;break;
			case 'E': y=1;
		}
		int x1,y1;
        for(int j=1;;j++){
        	x1=j*x+cas[i].x;   
        	y1=j*y+cas[i].y;
        	if(x1<0 || x1>m || y1<0 || y1>n)break;
        	if(g[x1][y1])break;            
        	if(j%cas[i].v==0){  //子弹会停留的坐标,时间=步数/速度 
        		for(int h=j/cas[i].v;h<=d;h+=cas[i].t){
        			remb[h][x1][y1]=true;
				}
			}
		}
  }
}
bool judge(node &nd){   //当逃到某点时,明显无法到终点时,剪枝 
	if(nd.time>d)return false;
	int temp=abs(nd.x-m)+abs(nd.y-n);
	return d-nd.time>=temp;
}
void bfs(){
	memset(vis,false,sizeof(vis));
	queue<node>q;
	q.push(node(0,0,0));
	vis[0][0][0]=true;
	while(!q.empty()){
		node u=q.front();
		q.pop();
		if(u.x==m && u.y==n){
			printf("%d\n",u.time);
			return ;
		}
		for(int i=0;i<5;i++){
			node v=node(u.x+dir[i][0],u.y+dir[i][1],u.time+1);
			if(isValid(v) && !vis[v.time][v.x][v.y] && !remb[v.time][v.x][v.y] && judge(v) && !g[v.x][v.y]){
				q.push(v);
				vis[v.time][v.x][v.y]=true;
			}
		}
	}
	printf("Bad luck!\n");
}
int main(){
	while(scanf("%d%d%d%d",&m,&n,&k,&d)==4){
		getchar();
		memset(g,false,sizeof(g));
		for(int i=0;i<k;i++){
			scanf("%c",&cas[i].c);
			scanf("%d%d%d%d",&cas[i].t,&cas[i].v,&cas[i].x,&cas[i].y);
			getchar();    //吃掉换行 
			g[cas[i].x][cas[i].y]=true;
		}
		solve();
		bfs();
	}
	return 0;
} 

耗时 400ms

posted @ 2020-06-12 15:26  RioTian  阅读(394)  评论(0编辑  收藏  举报