【ybtoj】【BFS】【例题4】荆轲刺秦王

【例题4】荆轲刺秦王


Link

传送门
题目


解题思路

wcnmd,我就无语了,搞了我一天的题,就nm数组开小了,wcnm

守卫监视范围
差分;但我不会,这题数据太淼,直接暴力枚举标记就OK了,在洛谷不行

对于每一个点记录{x,y,c1使用次数,c2使用次数,步数t}

对于一个点,向它能走到的点尝试扩展

  • 没有用瞬移
    1. 新点没有士兵监视,不是士兵,{x + wax[i], y + way[i], c1, c2, t+1}
    2. 如果新点是士兵监视,但不是士兵,{x + wax[i], y + way[i], c1+1, c2, t+1}
  • 用了瞬移
    1. 新点没有士兵监视,不是士兵,{x + wax[i] * d, y + way[i] * d, c1, c2+1, t+1}
    2. 如果新点是士兵监视,但不是士兵,{x + wax[i] * d, y + way[i] * d, c1+1, c2+1, t+1}

剪枝

  • 走到一个点可能有很多种走法,所以一个点的判重,要判[x][y][c1][c2]有无走过
  • 答案的优良性包括{t,c1,c2},so第一次走到终点不一定是最优的,但是如果当前队列层的步数已经超过当前最优答案了,就可以直接退出了

如果方向是偶数,可以尝试瞬移
在这里插入图片描述


Code

#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>

using namespace std;

struct DT{
    
    
	int x, y, c1,c2, t;
}now, ans;
const int wax[8] = {
    
    -1, -1, 0, 1, 1, 1, 0, -1}, way[8] = {
    
    0, 1, 1, 1, 0, -1, -1, -1};
queue<DT>q;
string s;
int n, m, c1, c2, d, sx, sy, ex, ey, xx, yy, a[355][355], b[355][355], v[355][355][16][16];

bool check(int x, int y) {
    
     return(x > 0 && y > 0 && x <= n && y <= m); }

void answer(int ax, int ay, int at, int ac1, int ac2) {
    
    
	if (ax == ex && ay == ey) {
    
    
		if (at < ans.t || ans.t == -1) {
    
    //比当前答案优,或第一次到达终点
			ans.t = at;
			ans.c1 = ac1, ans.c2 = ac2;
		}else {
    
    
			if ((ac1 + ac2) < (ans.c1 + ans.c2))//技能数和更少
				ans.c1 = ac1, ans.c2 = ac2;
			else
			if ((ac1 + ac2) == (ans.c1 + ans.c2) && c1 < ans.c1)//隐身更少
				ans.c1 = ac1, ans.c2 = ac2;
		}
	}
}

void bfs() {
    
    
	v[sx][sy][0][0] = 1, ans.t = -1;
	q.push((DT){
    
    sx, sy, 0, 0, 0});
	while (!q.empty()) {
    
    
		now = q.front();
		if (now.t > ans.t && ans.t != -1) {
    
    //已经超过当前最优答案了
			q.pop();
			break;
		}
		answer(now.x, now.y, now.t, now.c1, now.c2);//尝试更新答案
		for (int i = 0; i < 8; i++) {
    
    
			xx = now.x + wax[i], yy = now.y + way[i];
			if (check(xx, yy))//不使用瞬移
				if (!a[xx][yy]) {
    
    //不在守卫监视范围——不使用隐身
					if (!v[xx][yy][now.c1][now.c2]) {
    
    
						q.push((DT){
    
    xx, yy, now.c1, now.c2, now.t + 1});
						v[xx][yy][now.c1][now.c2] = 1;
					}
				}else if (!b[xx][yy] && now.c1 < c1){
    
    //在守卫监视范围,不是守卫——使用隐身
					if (!v[xx][yy][now.c1 + 1][now.c2]) {
    
    
						q.push((DT){
    
    xx, yy, now.c1 + 1, now.c2, now.t + 1});
						v[xx][yy][now.c1 + 1][now.c2] = 1;
					}
				}
			if (!(i % 2) && now.c2 < c2) {
    
    //使用瞬移,↓同上
				xx = now.x + wax[i] * d, yy = now.y + way[i] * d;
				if (check(xx, yy))
					if (!a[xx][yy]) {
    
    
						if (!v[xx][yy][now.c1][now.c2 + 1]) {
    
    
							q.push((DT){
    
    xx, yy, now.c1, now.c2 + 1, now.t + 1});
							v[xx][yy][now.c1][now.c2 + 1] = 1;
						}
					}else if (!b[xx][yy] && now.c1 < c1){
    
    
							if (!v[xx][yy][now.c1 + 1][now.c2 + 1]) {
    
    
								q.push((DT){
    
    xx, yy, now.c1 + 1, now.c2 + 1, now.t + 1});
								v[xx][yy][now.c1 + 1][now.c2 + 1] = 1;
							}
						}
			}
		}
		q.pop();
	}
}

int main() {
    
    
	scanf ("%d%d%d%d%d", &n, &m, &c1, &c2, &d);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
    
    
			cin >> s;
			if (s == ".") continue;
			if (s == "S") {
    
    
				sx = i, sy = j;
				continue;
			}
			if (s == "T") {
    
    
				ex = i, ey = j;
				continue;
			}
			
			int u = s[0] - 48;
			for (int ii = 1; ii < s.size(); ii++)
				u = u * 10 + (s[ii] - 48);
			for (int ii = max(1, i - u + 1); ii <= min(n, i + u - 1); ii++)
				for (int jj = max(1, j - u + 1); jj <= min(m, j + u - 1); jj++)
					if ((abs(ii - i) + abs(jj - j)) < u)
						a[ii][jj]++;//标记守卫监视范围
			b[i][j] = 1;//标记守卫位置
		}
	bfs();
	if (ans.t == -1)
		printf ("-1");
	else printf ("%d %d %d", ans.t, ans.c1, ans.c2);
} 

猜你喜欢

转载自blog.csdn.net/qq_39940018/article/details/112969491