FOJ 2186 小明的迷宫(bfs+状压)

Problem 2186 小明的迷宫

Accept: 54    Submit: 162
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 3 0 0 0 0 100 0 0 0 0 2 2 1 1 1 1

 Sample Output

4 4

 Source

FOJ有奖月赛-2015年03月

 

 

题目:小明的迷宫
题意:一个n*m的迷宫,每个格子里有一个值。
正数代表财宝(财宝的个数不超过10);负数代表墙,无法通过; 0代表通道;
每次移动到相邻的格子,所花费的时间是1秒.小明只能按上下左右四个方向移动。
小明的初始位置是(1,1),迷宫的出口也在(1,1).
求获得所有财宝并逃出迷宫所花费的最小时间,如果无法完成目标则输出-1.
思路:BFS + 状压DP
dp[i][j]表示小明访问完i代表的几个宝藏以后,处在第j个宝藏所在房间时,总共花费的时间
用两个BFS预处理从第i个宝藏到第j个宝藏所在房间的最小时间dist[i][j],和起点到各个宝藏的时间mint[i][j]
然后用状压处理dp
最后要记得加上返回(1,1)的时间

 代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 110;
const int INF = 1e8; // 定义无穷大
struct point
{
    int x;
    int y;
    int time;
};
int map[N][N],dp[N][1<<11];
int mint[N][N];  //记录到达某点的最短时间
int dist[N][N]; //记录财宝之间的最短时间
queue<point>q;
int X[4]= {-1,1,0,0};
int Y[4]= {0,0,-1,1};
int n,m,cnt,dui[N][N];//dui[i][j]=k数组表示i,j坐标有一个编号为k的财宝
point start,end,no[20];
int bfs(int xx,int yy)//单点全图bfs
{
    memset(mint,-1,sizeof(mint));
    while(!q.empty())q.pop();
    start.x = xx,start.y = yy;
    mint[start.x][start.y] = 0;
    start.time = 0;
    q.push(start);
    point hd,next;
    int res = 0;
    while (!q.empty())
    {
        hd = q.front();
        q.pop();
        //printf("## x = %d , y = %d  , !!%d\n",hd.x,hd.y,res);
        if(map[hd.x][hd.y] > 0)res += map[hd.x][hd.y];//这个要放这,放for里会错!
        for(int k=0; k<4; k++)
        {
            next.x = hd.x+X[k];
            next.y = hd.y+Y[k];
            next.time  = hd.time + 1;
            int bao = map[next.x][next.y];
            if(bao >= 0 && next.x >= 1 && next.x <= n && next.y >= 1 && next.y <= m && mint[next.x][next.y]==-1)
            {
                mint[next.x][next.y] = next.time;
                q.push(next);
            }
        }
    }
    return res;
}
int vis[N][N];
int bfs2(int x1,int y1,int x2,int y2)//两点的最短时间
{
    memset(vis,-1,sizeof(vis));
    while(!q.empty())q.pop();
    start.x = x1,start.y = y1;
    vis[start.x][start.y] = 0;
    start.time = 0;
    q.push(start);
    point hd,next;
    while (!q.empty())
    {
        hd = q.front();
        q.pop();
        if(hd.x == x2 && hd.y == y2)return vis[x2][y2];
        for(int k=0; k<4; k++)
        {
            next.x = hd.x+X[k];
            next.y = hd.y+Y[k];
            next.time  = hd.time + 1;
            int bao = map[next.x][next.y];
            if(bao >= 0 && next.x >= 1 && next.x <= n && next.y >= 1 && next.y <= m && vis[next.x][next.y]==-1)
            {
                vis[next.x][next.y] = next.time;
                q.push(next);
            }
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        int cnt = 0,sum = 0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                cin >> map[i][j];
                if(map[i][j] > 0)
                {
                    no[cnt].x = i,no[cnt].y = j,dui[i][j] = cnt++;
                    sum += map[i][j];
                }
            }
        }
        int res = bfs(1,1);
        if(res < sum || map[1][1] < 0)
        {
            puts("-1");
            continue;
        }
        if(sum == 0)
        {
            puts("0");
            continue;
        }
        for(int i=0; i<cnt; i++)
            for(int j=i; j<cnt; j++)
            {
                if(i == j)dist[i][j] = 0;
                dist[i][j] = dist[j][i] = bfs2(no[i].x,no[i].y,no[j].x,no[j].y);
            }
        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] = mint[no[i].x][no[i].y];//这里先加上从(1,1)出发到各点的时间
        for(int j=0; j<(1 << cnt); j++)//这里和下一行的顺序一定不能反....找了一天的错误
            for(int i=0; i<cnt; i++)
                if(dp[i][j] != INF)
                {
                    for(int k=0; k<cnt; k++)
                    {
                        if((1<<k) & j)continue;
                        dp[k][(1<<k)|j] = min(dp[k][(1<<k)|j],dp[i][j] + dist[i][k]);
                    }
                }
        int ans = INF;
        for(int i=0; i<cnt; i++)
            ans = min(ans , dp[i][(1<<cnt)-1] + mint[no[i].x][no[i].y]);//再加上回来的时间,会与出发点不同
        printf("%d\n",ans);
    }
    return 0;
}
View Code
posted @ 2015-03-23 22:42  Doli  阅读(180)  评论(0编辑  收藏  举报