zoukankan      html  css  js  c++  java
  • UVALive 2147 Push!!(队列实现DP)

      就我的理解来说这个题,本质上是一个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
    ",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
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Mysql update case
    phpexcel导出excel等比例缩放图片
    phpexcel错误 You tried to set a sheet active by the out of bounds index: 1解决办法
    phpexcel操作
    Java io基础
    java线程基础
    java 集合基础(适用单线程)
    java 泛型深入
    Java反射基础
    Java泛型基础
  • 原文地址:https://www.cnblogs.com/jifahu/p/5689810.html
Copyright © 2011-2022 走看看