码迷,mamicode.com
首页 > 其他好文 > 详细

UVALive 2147 Push!!(队列实现DP)

时间:2016-07-20 22:52:43      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:

  就我的理解来说这个题,本质上是一个DP题,不应该说是搜索,因为我的做法是把表格中所有的数据都找到,使用队列暴力来遍历出所有状态,因为题目中的数据范围小,所有耗时也小。

  首先分析箱子是一个被动物体,人是主动物体,箱子的移动取决于人的移动,所以在bfs中只需要让人去移动,进而带动箱子就可以了。我们使用dp[x1][y1][x2][y2]来记录状态,分别代表人和箱子的位置。在队列实现DP的过程中,我们必须要把当前所在的情况标记为未走过,这个很重要。有人可能会质疑,这样可能导致无限的入队列,导致死循环,所以需要一个技巧,首先新的状态是否更新跟这个状态是否被标记无关,所以能否让这个点入队列,需要首先满足状态值得更新的条件,所以我们不可能去走无意义的回头路,不可能会产生走过来走过去,从而导致死循环的情况。然后就要满足这个点未被标记的情况,才能入队列,标记这个点。

  当然有人也会问,只要把状态更新了就可以了,干嘛非要入队列呢?如果不仅队列,更新的仅仅是这个状态,其他的状态无法经过他转移,无法获得最优子结构,就不符合DP的思想,就是错误的了。(当然这个地方可能也仅仅是我不懂,但是这个标记方法还是很重要的),我给出一个例子吧。

0 0 0 0 0 0

0 0 1 1 1 0

0 0 1 4 0 0

0 0 1 2 1 0

0 0 0 0 3 0

0 0 0 0 1 0

0 0 0 0 0 0

我相信你很快就能找出两种方法来,往上走耗费的步数多,在bfs过程中也是后来达到的点,所以bfs第一次到两个加粗的0的时候,耗费步数少,但是推的次数多,而题目中要求最小的推数,与走的步数无关,所以这点必须更新并且入队列更新其他的点,如果我们没有把以前的标记消除,导致入队列失败,更新失败,答案出错。

最后一句话总结,在队列实现dp的过程中,一定记着把以前的标记消除,让新的点入队列,更新其他的状态,才能得到最后的最优解。

代码如下:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
#define maxn 10
#define INF 999999999
int dp[maxn][maxn][maxn][maxn],n,m,maps[maxn][maxn];
int go[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int vis[maxn][maxn][maxn][maxn],endx,endy;
struct Node
{
    int x1,y1,x2,y2;
    Node () {};
    Node(int xx1,int yy1,int xx2,int yy2)
    {
        x1 = xx1;
        y1 = yy1;
        x2 = xx2;
        y2 = yy2;
    }
};
void mark(Node a)
{
    vis[a.x1][a.y1][a.x2][a.y2] = 1;
}
bool ok(int x,int y)
{
    return (x>=0&&x<n && y>=0&&y<m && maps[x][y]!=1);
}
int bfs(Node start)
{
    queue<Node> que;
    memset(vis,0,sizeof(vis));
    while(!que.empty()) que.pop();
    que.push(start);
    dp[start.x1][start.y1][start.x2][start.y2] = 0;
    //mark(start);
    while(!que.empty())
    {
        Node now = que.front();
        que.pop();
        int x1 = now.x1,y1 = now.y1;
        int x2 = now.x2,y2 = now.y2;
        vis[x1][y1][x2][y2] = 0;///进入队列,消除标记
        int xx1,xx2,yy1,yy2;
        for(int i = 0; i < 4; i++)
        {
            xx1 = x1 + go[i][0];
            yy1 = y1 + go[i][1];
            if(ok(xx1,yy1))
            {
                if(xx1 == x2 && yy1 == y2)
                {
                    xx2 = x2 + go[i][0];
                    yy2 = y2 + go[i][1];
                    if(ok(xx2,yy2))
                    {
                        if(dp[xx1][yy1][xx2][yy2]==-1 || dp[xx1][yy1][xx2][yy2]>dp[x1][y1][x2][y2]+1)///第一层检验
                        {
                            dp[xx1][yy1][xx2][yy2] = dp[x1][y1][x2][y2]+1;///更新当前点状态
                            if(!vis[xx1][yy1][xx2][yy2])
                            {
                                Node nxt(xx1,yy1,xx2,yy2);
                                mark(nxt);///进入队列,更新其他点
                                que.push(nxt);
                            }
                        }


                    }
                }
                else
                {
                    if(dp[xx1][yy1][x2][y2]== -1 || dp[xx1][yy1][x2][y2]>dp[x1][y1][x2][y2])
                    {
                        dp[xx1][yy1][x2][y2] = dp[x1][y1][x2][y2];
                        if(!vis[xx1][yy1][x2][y2])
                        {
                            Node nxt(xx1,yy1,x2,y2);
                            mark(nxt);
                            que.push(nxt);
                        }
                    }
                }
            }
        }
    }
    int ans = INF;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            //printf("dp[%d][%d] = %d\n",i,j,dp[i][j][endx][endy]);
            if(dp[i][j][endx][endy] != -1)
                ans = min(ans,dp[i][j][endx][endy]);
        }
    }
    if(ans != INF) return ans;
    return -1;
}
int main()
{
    while(~scanf("%d%d",&m,&n))
    {
        if(n+m == 0) break;
        Node start;
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < m; j++)
            {
                scanf("%d",&maps[i][j]);
                if(maps[i][j] == 2)
                {
                    start.x2 = i;
                    start.y2 = j;
                }
                if(maps[i][j] == 4)
                {
                    start.x1 = i;
                    start.y1 = j;
                }
                if(maps[i][j] == 3)
                {
                    endx = i;
                    endy = j;
                }
            }
        }
        memset(dp,-1,sizeof(dp));
        int ans = bfs(start);
        printf("%d\n",ans);
    }
    return 0;
}

 

UVALive 2147 Push!!(队列实现DP)

标签:

原文地址:http://www.cnblogs.com/jifahu/p/5689810.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!