Problem 2186 小明的迷宫Accept: 86 Submit: 254
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
小明误入迷宫,塞翁失马焉知非福,原来在迷宫中还藏着一些财宝,小明想获得所有的财宝并离开迷宫。因为小明还是学生,还有家庭作业要做,所以他想尽快获得所有财宝并离开迷宫。
Input
有多组测试数据。
每组数据第一行给出两个正整数n,m(0<n,m<=100)。代表迷宫的长和宽。
接着n行,每行m个整数。正数代表财宝(财宝的个数不超过10);负数代表墙,无法通过;0代表通道。
每次移动到相邻的格子,所花费的时间是1秒。小明只能按上、下、左、右四个方向移动。
小明的初始位置是(1,1)。迷宫的出口也在(1,1)。
Output
输出获得所有财宝并逃出迷宫所花费的最小时间,如果无法完成目标则输出-1。
Sample Input
3 30 0 00 100 00 0 02 21 11 1
Sample Output
44
Source
FOJ有奖月赛-2015年03月分析:设宝藏数目为N,用BFS将问题转化为N+1个点(N个宝藏加上起/终点(1,1))的完全图上求(从起点开始遍历所有宝藏后又回到起点的)最短路径(使用状压DP)。
定义dp[i][j]表示状态是j的时候走到的最后一个是i的最小消费时间
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 105;
typedef long long LL;
const int INF = 0xfffffff;
int N, M;
int a[maxn][maxn], X[15], Y[15];
int dp[11][1 << 11];
bool vis[maxn][maxn];
int dis[15][15];
int d[maxn][maxn];
queue<int> q;
int dirx[4] = { 0, 0, 1, -1 };
int diry[4] = { 1, -1, 0, 0 };
int bfs(int x1, int y1)
{
memset(vis, false, sizeof(vis));
while (!q.empty()) q.pop();
int sum = 0;
vis[x1][y1] = true;
if (a[x1][y1]>0) sum += a[x1][y1];
int t = x1*M + y1;
q.push(t);
while (!q.empty())
{
int u = q.front(); q.pop();
int x = u / M, y = u % M;
for (int i = 0; i < 4; i++)
{
int xx = x + dirx[i];
int yy = y + diry[i];
if (xx < 0 || N <= xx || yy < 0 || M <= yy || vis[xx][yy] == true || a[xx][yy]<0) continue;
vis[xx][yy] = true;
if (a[xx][yy]>0) sum += a[xx][yy];
q.push(xx*M + yy);
}
}
return sum;
}
int BFS(int x1, int y1, int x2, int y2)
{
memset(vis, false, sizeof(vis));
memset(d, 0, sizeof(d));
while (!q.empty()) q.pop();
vis[x1][y1] = true;
if (x1 == x2&&y1 == y2) return 0;//第一次访问
int t = x1*M + y1;//用整形t保存二维图上的一点的坐标,注意M个列的下标需要取{0,1,2,...,M-1}
q.push(t);
while (!q.empty())
{
int u = q.front(); q.pop();
int x = u / M, y = u % M;
for (int i = 0; i < 4; i++)
{
int xx = x + dirx[i];
int yy = y + diry[i];
if (xx < 0 || N <= xx || yy < 0 || M <= yy || vis[xx][yy] == true || a[xx][yy]<0) continue;//条件:没有越界,没有访问过,不是障碍
vis[xx][yy] = true;
d[xx][yy] = d[x][y] + 1;
if (xx == x2&&yy == y2) return d[xx][yy];
q.push(xx*M + yy);
}
}
}
int main()
{
freopen("f:\\input.txt", "r", stdin);
while (~scanf("%d%d", &N, &M))
{
int cnt = 0, sum = 0;
for (int i = 0; i < N;i++)
for (int j = 0; j < M; j++)
{
scanf("%d", &a[i][j]);
if (a[i][j]>0) X[cnt] = i, Y[cnt++] = j, sum += a[i][j];
}
if (sum == 0)
{
printf("0\n");
continue;
}
if (a[0][0] < 0)
{
printf("-1\n");
continue;
}
if (bfs(0, 0) != sum)
{
printf("-1\n");
continue;
}
X[cnt] = Y[cnt] = 0;
for (int i = 0; i <= cnt; i++)
for (int j = i; j <= cnt; j++)
{
if (i == j)
dis[i][j] = 0;
else
dis[i][j] = dis[j][i] = BFS(X[i], Y[i], X[j], Y[j]);
}
for(int i = 0; i < cnt;i++)
for (int j = 0; j < (1 << cnt); j++)
dp[i][j] = INF;
for (int i = 0; i < cnt; i++)
dp[i][1 << i] = dis[cnt][i];
for (int j = 1; j < (1<<cnt);j++)
for (int i = 0; i < cnt; i++)//从dp[i][j]开始更新它的儿子
{
if (dp[i][j] == INF)continue;
for (int k = 0; k < cnt; k++)//从宝藏i走到宝藏k
{
if ((j&(1 << i)) == 0)continue;
if (i == k)continue;
if ((j&(1 << k)) == 0)//可以进行第i个宝藏的更新
{
dp[k][j | (1 << k)] = min(dp[k][j | (1 << k)], dp[i][j] + dis[i][k]);
}
}
}
int ans = INF;
for (int i = 0; i < cnt; i++)
ans = min(ans, dp[i][(1 << cnt) - 1] + dis[cnt][i]);//
printf("%d\n", ans);
}
return 0;
}

本文介绍了一道算法题“小明的迷宫”,利用BFS搜索算法结合状态压缩动态规划解决迷宫中寻找所有宝藏并返回起点的问题。通过分析问题特点,设计了高效的算法流程。
小明的迷宫&spm=1001.2101.3001.5002&articleId=44701859&d=1&t=3&u=05ac3a45eae14919966e647a64a4912e)
5334

被折叠的 条评论
为什么被折叠?



