Dragon slayer(搜索板题)

这篇博客介绍了2022年'杭电杯'算法设计超级联赛的一道题目——Dragonslayer。问题背景是在一个有墙的网格图中,求从起点到终点的最短体力消耗路径。博主提供了两种解决方案,一是通过DFS枚举所有可能的墙破坏情况,二是使用状态压缩优化。每种方法都涉及到了坐标转换、BFS路径检测和复杂度分析,最后给出了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:

2022“杭电杯”中国大学生算法设计超级联赛(1)

Dragon slayer

大致题意:

一个n*m的网格图

人在方块中央 每次从一个方块中央走到另一个方块中央

有k个墙 墙在线上 墙只有水平的和竖直的

人从一个方块到另一个方块时不能越过墙

人每消耗1体力可以完全打破一堵墙 完全打破一堵墙会让这堵墙完全消失

不只是这次越过的部分 而是整个墙都会消失

墙可以重叠 越过重叠的部分时 这部分的每堵墙都需要消耗体力打破

问人从 以(xs,ys)为左下角的方块中央 走到 以(xt,yt)为左下角的方块中央

至少需要消耗多少体力

大致思路:

1.坐标处理

人的坐标在方格中央 (xs+0.5,ys+0.5) 不方便计算 因此将整个坐标系*2

图 n * m → 2*n * 2*m

人 (xs+0.5,ys+0.5) → (2*xs+1,2*ys+1)

墙 (x1,y1)-(x2,y2) → (2*x1,2*y1)-(2*x2,2*y2)

2.DFS枚举所有情况 从中找出可行解中的最优解

k个墙 每个墙有打破/没打破2种情况 一共2^k种情况

其中部分情况 人无法从起点到达终点 不考虑

还有部分情况 人可以从起点到达终点 从这些情况中找出最优解

3.BFS检测每种情况能否从起点到达终点

在DFS时提前在图中处理好墙 之后朴实无华的BFS

复杂度:t * 2^k * n*m

t<=10        k,n,m<=15        总复杂度<=73728000<8e7

AC代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using db = double;
#define edl '\n'
#define str string
#define pll pair<ll, ll>
#define fir first
#define sec second
#define heap priority_queue
#define SPO(n) fixed << setprecision(n)
#define FOR(i, l, r) for (ll i = l; i <= r; ++i)
#define ROF(i, r, l) for (ll i = r; i >= l; --i)
#ifdef debugcmd
#define DBG(n) cout << "!!! " << #n << ": " << n << edl
#else
#define DBG(n) ;
#endif
// const db PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const long long LNF = 0x3f3f3f3f3f3f3f3f;
const db EPS = 1.0e-9;
const ll MOD = 1e9 + 7;
const ll MXN = 55;
ll n, m, k;
ll xs, ys, xt, yt;
struct wnd
{
	ll x1, y1, x2, y2;
};
wnd wall[MXN];
ll mp[MXN][MXN];
bool vis[MXN][MXN];
queue<pll> q;
ll dx[5] = {0, 0, 0, -1, 1};
ll dy[5] = {0, 1, -1, 0, 0};
ll ans;

void Modify(ll i, ll v)
{
	ll ldx = min(wall[i].x1, wall[i].x2);
	ll ldy = min(wall[i].y1, wall[i].y2);
	ll rux = max(wall[i].x1, wall[i].x2);
	ll ruy = max(wall[i].y1, wall[i].y2);
	FOR(i, ldx, rux)
	FOR(j, ldy, ruy)
	mp[i][j] += v;
	return;
}
void BFS(ll cost)
{
	memset(vis, false, sizeof(vis));
	while (!q.empty())
		q.pop();
	pll u, v, md;
	u.fir = xs;
	u.sec = ys;
	vis[u.fir][u.sec] = true;
	q.push(u);
	while (!q.empty())
	{
		u = q.front();
		q.pop();
		FOR(i, 1, 4)
		{
			v.fir = u.fir + 2 * dx[i];
			v.sec = u.sec + 2 * dy[i];
			md.fir = u.fir + dx[i];
			md.sec = u.sec + dy[i];
			if (0 <= v.fir && v.fir <= n &&
				0 <= v.sec && v.sec <= m &&
				vis[v.fir][v.sec] == false &&
				mp[md.fir][md.sec] == 0)
			{
				vis[v.fir][v.sec] = true;
				q.push(v);
			}
		}
	}
	if (vis[xt][yt] == true)
		ans = cost;
	return;
}
void DFS(ll i, ll sum)
{
	if (sum >= ans)
		return;
	if (i == k)
	{
		BFS(sum);
		return;
	}
	Modify(i + 1, 1);
	DFS(i + 1, sum);
	Modify(i + 1, -1);
	DFS(i + 1, sum + 1);
	return;
}
void Solve(void)
{
	memset(mp, 0, sizeof(mp));
	ans = LNF;
	cin >> n >> m >> k;
	n *= 2;
	m *= 2;
	cin >> xs >> ys >> xt >> yt;
	xs = 2 * xs + 1;
	ys = 2 * ys + 1;
	xt = 2 * xt + 1;
	yt = 2 * yt + 1;
	FOR(i, 1, k)
	{
		cin >> wall[i].x1 >> wall[i].y1 >> wall[i].x2 >> wall[i].y2;
		wall[i].x1 *= 2;
		wall[i].y1 *= 2;
		wall[i].x2 *= 2;
		wall[i].y2 *= 2;
	}
	DFS(0, 0);
	cout << ans << edl;
	return;
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	// #ifdef cincoutcmd
	// freopen("cin.txt","r",stdin);
	// freopen("cout.txt","w",stdout);
	// #endif
	ll t;
	cin >> t;
	while (t--)
		Solve();
	return 0;
}

状态压缩代替DFS:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using db = double;
#define edl '\n'
#define str string
#define pll pair<ll, ll>
#define fir first
#define sec second
#define heap priority_queue
#define SPO(n) fixed << setprecision(n)
#define FOR(i, l, r) for (ll i = l; i <= r; ++i)
#define ROF(i, r, l) for (ll i = r; i >= l; --i)
#ifdef debugcmd
#define DBG(n) cout << "!!! " << #n << ": " << n << edl
#else
#define DBG(n) ;
#endif
// const db PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const long long LNF = 0x3f3f3f3f3f3f3f3f;
const db EPS = 1.0e-9;
const ll MOD = 1e9 + 7;
const ll MXN = 55;
ll n, m, k;
ll xs, ys, xt, yt;
struct wnd
{
	ll x1, y1, x2, y2;
};
wnd wall[MXN];
ll mp[MXN][MXN];
bool vis[MXN][MXN];
queue<pll> q;
ll dx[5] = {0, 0, 0, -1, 1};
ll dy[5] = {0, 1, -1, 0, 0};
ll ans;

inline ll Lowbit(ll x)
{
	return x & (-x);
}
void Modify(ll i, ll v)
{
	ll ldx = min(wall[i].x1, wall[i].x2);
	ll ldy = min(wall[i].y1, wall[i].y2);
	ll rux = max(wall[i].x1, wall[i].x2);
	ll ruy = max(wall[i].y1, wall[i].y2);
	FOR(i, ldx, rux)
	FOR(j, ldy, ruy)
	mp[i][j] += v;
	return;
}
void BFS(ll cost)
{
	memset(vis, false, sizeof(vis));
	while (!q.empty())
		q.pop();
	pll u, v, md;
	u.fir = xs;
	u.sec = ys;
	vis[u.fir][u.sec] = true;
	q.push(u);
	while (!q.empty())
	{
		u = q.front();
		q.pop();
		FOR(i, 1, 4)
		{
			v.fir = u.fir + 2 * dx[i];
			v.sec = u.sec + 2 * dy[i];
			md.fir = u.fir + dx[i];
			md.sec = u.sec + dy[i];
			if (0 <= v.fir && v.fir <= n &&
				0 <= v.sec && v.sec <= m &&
				vis[v.fir][v.sec] == false &&
				mp[md.fir][md.sec] == 0)
			{
				vis[v.fir][v.sec] = true;
				q.push(v);
			}
		}
	}
	if (vis[xt][yt])
		ans = cost;
	return;
}
void Solve(void)
{
	memset(mp, 0, sizeof(mp));
	ans = LNF;
	cin >> n >> m >> k;
	n *= 2;
	m *= 2;
	cin >> xs >> ys >> xt >> yt;
	xs = 2 * xs + 1;
	ys = 2 * ys + 1;
	xt = 2 * xt + 1;
	yt = 2 * yt + 1;
	FOR(i, 1, k)
	{
		cin >> wall[i].x1 >> wall[i].y1 >> wall[i].x2 >> wall[i].y2;
		wall[i].x1 *= 2;
		wall[i].y1 *= 2;
		wall[i].x2 *= 2;
		wall[i].y2 *= 2;
	}
	ll lim = (1 << k) - 1;
	FOR(i, 0, lim)
	{
		ll sum = k;
		for (ll j = i; j > 0; j -= Lowbit(j))
			--sum;
		if (sum >= ans)
			continue;
		FOR(j, 1, k)
		if ((i >> (j - 1)) & 1)
			Modify(j, 1);
		BFS(sum);
		FOR(j, 1, k)
		if ((i >> (j - 1)) & 1)
			Modify(j, -1);
	}
	cout << ans << edl;
	return;
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	// #ifdef cincoutcmd
	// freopen("cin.txt","r",stdin);
	// freopen("cout.txt","w",stdout);
	// #endif
	ll t;
	cin >> t;
	while (t--)
		Solve();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值