您的位置:首页 > 其它

(BFS,状压DP)小明的迷宫

2015-03-28 15:27 218 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: