hdu 2128 Tempter of the Bone II ( bfs+不好搞的判重 )

Tempter of the Bone II

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 98304/32768 K (Java/Others)
Total Submission(s): 1128    Accepted Submission(s): 282


Problem Description
The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze was changed and the way he came in was lost.He realized that the bone was a trap, and he tried desperately to get out of this maze.


The maze was a rectangle with the sizes of N by M. The maze is made up of a door,many walls and many explosives. Doggie need to reach the door to escape from the tempter. In every second, he could move one block to one of the upper, lower, left or right neighboring blocks. And if the destination is a wall, Doggie need another second explode and a explosive to explode it if he had some explosives. Once he entered a block with explosives,he can take away all of the explosives. Can the poor doggie survive? Please help him.
 

Input
The input consists of multiple test cases. The first line of each test case contains two integers N, M,(2 <= N, M <= 8). which denote the sizes of the maze.The next N lines give the maze layout, with each line containing M characters. A character is one of the following:

'X': a block of wall;
'S': the start point of the doggie;
'D': the Door;
'.': an empty block;
'1'--'9':explosives in that block.

Note,initially he had no explosives.

The input is terminated with two 0's. This test case is not to be processed.
 

Output
For each test case, print the minimum time the doggie need to escape if the doggie can survive, or -1 otherwise.
 

Sample Input
  
  
4 4 SX.. XX.. .... 1..D 4 4 S.X1 .... ..XX ..XD 0 0
 

Sample Output
  
  
-1 9
 

Author
XHD
 

Source

感想:
这题其他的都简单,就是判重不好想。以前用的什么判重都不管用了,最后逼得我用了vector<Node>vis[maxn][maxn]才解决了问题。过得有点囧,跑到3000ms了。
ps:网上很多代码都是错的,因为杭电数据水了,所以就过了,这份代码是严密的。

题意:
在一个矩阵中,给一个起点一个终点,矩阵中有的位置有墙,有的位置有炸弹,对于墙必须拿炸弹炸才能过,每走一步花费1s,每炸一次门花1s,问从起点到终点的最短时间。

思路:
因为地图时刻在变,所以一般的判重不行。我是这样处理的:
用两个long long 记录走到当前位置时的 炸弹的状态stateb、墙的状态statew,如果再次走到同一点的话,判断此时的stb、stw与以前经过这点时的状态有没有完全相同的,有的话就不用进栈了,没有的话就进栈。这样就可以保证所有的状态都进栈了,虽然有些是无效的,但是不好判断哪些是无效的,所以全部进栈处理。

ps:思路来源于:http://blog.csdn.net/acm_cxlove/article/details/7635497

代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>
#define maxn 10
using namespace std;

int n,m,ans;
int sx,sy,ex,ey;
long long stw,stb,curstw,curstb;
int mp[maxn][maxn];
int dx[]= {-1,1,0,0};
int dy[]= {0,0,-1,1};
char s[maxn];
struct Node
{
    int x,y;
    int step,bomb;
    long long statew;
    long long stateb;
    friend bool operator <(const Node&xx,const Node&yy)
    {
        return xx.step>yy.step;
    }
} cur,now;
vector<Node>vis[maxn][maxn];  // 位置+结构体判重
priority_queue<Node>q;

bool isok(int tx,int ty)  // 判重
{
    int i,j,sz;
    sz=vis[tx][ty].size();
    for(i=0; i<sz; i++)
    {
        if(vis[tx][ty][i].stateb==curstb&&vis[tx][ty][i].statew==curstw) return false ;
    }
    return true ;
}
bool bfs()
{
    int i,j,t,nx,ny,nstep,nbomb,tx,ty;
    long long nstw,nstb,tstw,tstb;
    while(!q.empty()) q.pop();
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=m; j++)
        {
            vis[i][j].clear();
        }
    }
    cur.x=sx;
    cur.y=sy;
    cur.step=0;
    cur.bomb=0;
    cur.stateb=stb;
    cur.statew=stw;
    vis[sx][sy].push_back(cur);
    q.push(cur);
    while(!q.empty())
    {
        now=q.top();
        q.pop();
        nx=now.x;
        ny=now.y;
        nstep=now.step;
        nbomb=now.bomb;
        nstb=now.stateb;
        nstw=now.statew;
        for(i=0; i<4; i++)
        {
            tx=nx+dx[i];
            ty=ny+dy[i];
            t=(tx-1)*m+ty-1;
            if(tx<1||tx>n||ty<1||ty>m) continue ;
            if(nbomb==0&&(nstw&(1LL<<t))) continue ; // 没有炸弹而此时为墙
            curstb=nstb;
            curstw=nstw;
            cur.bomb=nbomb;
            if(nstb&(1LL<<t)) curstb=nstb&~(1LL<<t); // 如果该位置有炸弹就获取炸弹 不能对mp处理 因为mp在变 要对当前的stb、stw处理
            if(nstw&(1LL<<t)) curstw=nstw&~(1LL<<t); // 如果该位置为墙就炸掉墙
            if(isok(tx,ty))
            {
                cur.x=tx;
                cur.y=ty;
                if(tx==ex&&ty==ey)  // 在这里判断终点要快一点 开始在外面判TLE了
                {
                    ans=nstep+1;
                    return true ;
                }
                cur.bomb=nbomb;
                if(nstb&(1LL<<t)) cur.bomb+=mp[tx][ty];
                if(nstw&(1LL<<t)) cur.bomb--;
                cur.stateb=curstb;
                cur.statew=curstw;
                cur.step=nstep+1;
                if(nstw&(1LL<<t)) cur.step++;
                vis[tx][ty].push_back(cur);
                q.push(cur);
            }
        }
    }
    return false ;
}
int main()
{
    int i,j,t;
    while(scanf("%d%d",&n,&m),n||m)
    {
        memset(mp,0,sizeof(mp));
        stw=stb=0;
        for(i=1; i<=n; i++)
        {
            scanf("%s",s);
            for(j=1; j<=m; j++)
            {
                t=(i-1)*m+j-1;
                if(s[j-1]=='S') sx=i,sy=j;
                else if(s[j-1]=='D') ex=i,ey=j;
                else if(s[j-1]=='X')
                {
                    mp[i][j]=-1;
                    stw=(1LL<<t)|stw;
                }
                else if(s[j-1]>='1'&&s[j-1]<='9')
                {
                    mp[i][j]=s[j-1]-'0';
                    stb=(1LL<<t)|stb;
                }
            }
        }
        if(bfs()) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}
/*
5 5
S1XX.
.XX..
XX.1.
...XX
...XD
6 5
S.XX1
X.1X1
XX.X.
XXXXX
XXXXX
XXXDX
2 6
S.1XXD
1..XXX
4 4
S1X1
XXXX
XXDX
XXXX
6 2
S1
..
1X
XX
XX
DX
8 8
......XD
.XXXX..X
.XXXXX..
.XXXXXXX
.XXXXXXX
...4XXXX
XXXXXXX3
XXXXXXXS

ans:
14
17
9
8
9
38
*/

代码2:
将炸弹和墙同时处理,会快一倍,优化到1400ms++了。
炸弹没取的时候状态可以看做墙,取了之后就变为空地了。所以地图就只有走和不走两个状态了,只用一个long long就能表示其状态了。这种处理也是严密的,想一想为什么?
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>
#define maxn 10
using namespace std;

int n,m,ans;
int sx,sy,ex,ey;
long long stw,curstw;
int mp[maxn][maxn];
int dx[]= {-1,1,0,0};
int dy[]= {0,0,-1,1};
char s[maxn];
struct Node
{
    int x,y;
    int step,bomb;
    long long statew;
    friend bool operator <(const Node&xx,const Node&yy)
    {
        return xx.step>yy.step;
    }
} cur,now;
vector<Node>vis[maxn][maxn];  // 位置+结构体判重
priority_queue<Node>q;

bool isok(int tx,int ty)  // 判重
{
    int i,j,sz;
    sz=vis[tx][ty].size();
    for(i=0; i<sz; i++)
    {
        if(vis[tx][ty][i].statew==curstw) return false ;
    }
    return true ;
}
bool bfs()
{
    int i,j,t,nx,ny,nstep,nbomb,tx,ty;
    long long nstw,tstw;
    while(!q.empty()) q.pop();
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=m; j++)
        {
            vis[i][j].clear();
        }
    }
    cur.x=sx;
    cur.y=sy;
    cur.step=0;
    cur.bomb=0;
    cur.statew=stw;
    vis[sx][sy].push_back(cur);
    q.push(cur);
    while(!q.empty())
    {
        now=q.top();
        q.pop();
        nx=now.x;
        ny=now.y;
        nstep=now.step;
        nbomb=now.bomb;
        nstw=now.statew;
        for(i=0; i<4; i++)
        {
            tx=nx+dx[i];
            ty=ny+dy[i];
            t=(tx-1)*m+ty-1;
            if(tx<1||tx>n||ty<1||ty>m) continue ;
            if(nbomb==0&&(nstw&(1LL<<t))&&mp[tx][ty]==-1) continue ; // 没有炸弹而此时为墙
            curstw=nstw;
            if((nstw&(1LL<<t))&&mp[tx][ty]) curstw=nstw&~(1LL<<t); // 如果该位置为墙就炸掉墙 有炸弹就获取
            if(isok(tx,ty))
            {
                cur.x=tx;
                cur.y=ty;
                if(tx==ex&&ty==ey)  // 在这里判断终点要快一点 开始在外面判TLE了
                {
                    ans=nstep+1;
                    return true ;
                }
                cur.bomb=nbomb;
                cur.statew=curstw;
                cur.step=nstep+1;
                if((nstw&(1LL<<t))&&mp[tx][ty])
                {
                    if(mp[tx][ty]==-1) cur.bomb--,cur.step++;
                    else cur.bomb+=mp[tx][ty];
                }
                vis[tx][ty].push_back(cur);
                q.push(cur);
            }
        }
    }
    return false ;
}
int main()
{
    int i,j,t;
    while(scanf("%d%d",&n,&m),n||m)
    {
        memset(mp,0,sizeof(mp));
        stw=0;
        for(i=1; i<=n; i++)
        {
            scanf("%s",s);
            for(j=1; j<=m; j++)
            {
                t=(i-1)*m+j-1;
                if(s[j-1]=='S') sx=i,sy=j;
                else if(s[j-1]=='D') ex=i,ey=j;
                else if(s[j-1]=='X')
                {
                    mp[i][j]=-1;
                    stw=(1LL<<t)|stw;
                }
                else if(s[j-1]>='1'&&s[j-1]<='9')
                {
                    mp[i][j]=s[j-1]-'0';
                    stw=(1LL<<t)|stw;
                }
            }
        }
        if(bfs()) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值