【算法练习】搜索BFS/DFS 百炼poj 4115:鸣人和佐助

题目地址:http://bailian.openjudge.cn/practice/4115

复习2019年9月4日 

注意剪枝的条件 还有最后判断flag

4115:鸣人和佐助

总时间限制: 

1000ms

 

内存限制: 

65536kB

描述

佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?

【算法练习】搜索BFS/DFS 百炼poj 4115:鸣人和佐助_第1张图片

已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?

输入

输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。

输出

输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。

样例输入

样例输入1
4 4 1
#@##
**##
###+
****

样例输入2
4 4 2
#@##
**##
###+
****

样例输出

样例输出1
6

样例输出2
4

内心os:这中二的题目@_@

理解题意:就是一个有附加条件的迷宫最短路。

一开始想要用BFS 然后发现好像是最短路,还有附加条件于是就开始写dfs,然鹅TLE

TLE的代码

#include 
#include 
#include 
using namespace std;
#define INF 0x3f3f3f
const int maxn=250;
int m,n,t;
int sx,sy,ex,ey,ans;  //起点 终点的坐标
bool f;
int go[][2]={1,0,
             -1,0,
             0,1,
             0,-1};
int visit[maxn][maxn];
char G[maxn][maxn];
/*struct Node{
    int x,y,time,a;
    Node() {}
    Node(int xx,int yy,int tt,int aa):x(xx),y(yy),time(tt),a(aa){}
};
queue Q;*/

void init(){
    memset(visit,0,sizeof(visit));
    ans=INF;
    f= false;
}

void read(){
    cin>>m>>n>>t;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>G[i][j];
            if(G[i][j]=='@'){
               sx=i;
               sy=j;
            }
            if(G[i][j]=='+'){
                ex=i;
                ey=j;
            }
        }
    }
}

bool inside(int x,int y){
    return x>=1 && x<=m && y>=1 && y<=n;
}
void DFS(int x,int y,int time,int a){
    if(x==ex && y==ey){
        f=true;
        if(ans>time)
            ans=time;
        return;
    }

    for(int i=0;i<4;i++){
        int xx=x+go[i][0];
        int yy=y+go[i][1];
        int tt=time+1;
        if(inside(xx,yy) && !visit[xx][yy] && tt=1){
                visit[xx][yy]=1;
                DFS(xx,yy,time+1,a-1);
                visit[xx][yy]=0;
            }
            else if(G[xx][yy]=='*' || G[xx][yy]=='+'){  //一开始忘记加+的条件
                visit[xx][yy]=1;
                DFS(xx,yy,time+1,a);
                visit[xx][yy]=0;
            }

        }
    }
}
int main(){
    init();
    read();
    DFS(sx,sy,0,t);

    if(f)  cout<

参考方法:https://zhuanlan.zhihu.com/p/61355033

剪枝 方法 ==>增加状态记录 在位置(x,y)时候金币数a时候的步数 ,如果再次碰到相同情况且步数> = 则直接return

“到当前位置消耗一定的查克拉时,所走的步数不能比以前走的多 ” (肯定是绕路了)

AC代码:

#include 
#include 
#include 
using namespace std;
#define INF 0x3f3f3f
const int maxn=250;
int m,n,t;
int sx,sy,ex,ey,ans;  //起点 终点的坐标
bool f;
int go[][2]={1,0,
             -1,0,
             0,1,
             0,-1};
int visit[maxn][maxn];
int step[maxn][maxn][maxn];
char G[maxn][maxn];
/*struct Node{
    int x,y,time,a;
    Node() {}
    Node(int xx,int yy,int tt,int aa):x(xx),y(yy),time(tt),a(aa){}
};
queue Q;*/

void init(){
    memset(visit,0,sizeof(visit));
    ans=INF;
    memset(step,0x3f,sizeof(step));
    f= false;
}

void read(){
    cin>>m>>n>>t;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>G[i][j];
            if(G[i][j]=='@'){
                sx=i;
                sy=j;
            }
            if(G[i][j]=='+'){
                ex=i;
                ey=j;
            }
        }
    }
}

bool inside(int x,int y){
    return x>=1 && x<=m && y>=1 && y<=n;
}
void DFS(int x,int y,int time,int a){

    if(time>=step[x][y][a]) return;
    //走到当前位置消耗一定的查克拉时,所走的步数不能比以前走的多

    if(x==ex && y==ey){
        f=true;
        if(ans>time)
            ans=time;
        return;
    }
    step[x][y][a]=time;

    for(int i=0;i<4;i++){
        int xx=x+go[i][0];
        int yy=y+go[i][1];
        int tt=time+1;
        if(inside(xx,yy) && !visit[xx][yy] && tt=1){
                visit[xx][yy]=1;
                DFS(xx,yy,time+1,a-1);
                visit[xx][yy]=0;
            }
            else if(G[xx][yy]=='*' || G[xx][yy]=='+' && tt

 

 

解题方法:BFS 之所以一开始没写成是因为不知道怎么处理visit 

发现别的blog的思路 用visit数组储存

参考链接:https://blog.csdn.net/curson_/article/details/52062695

这个写的很清晰,那个金币数(也就是查克拉)的理解好像还不太理解QAQ

 

AC代码 

一开始visit一定要初始化为 -1 ,要不然后面比较金币数

是当前金币数 > 之前走过下一位置的金币数

我分类讨论了一下,貌似不讨论也可 

用visit[x][y]表示经过点(x,y)后的金币数,如果下一个是要消耗金币那么就。。;

如果不需要消耗金币就。。。 

代码里有

#include 
#include 
#include 
using namespace std;
#define INF 0x3f3f3f
const int maxn=250;
int m,n,v;
int sx,sy,ex,ey,ans;  //起点 终点的坐标
bool f;
int go[][2]={1,0,
             -1,0,
             0,1,
             0,-1};

int visit[maxn][maxn];  //记录上一次走过时的金币
char G[maxn][maxn];
struct Node{
    int x,y,time,a;
    Node() {}
    Node(int xx,int yy,int tt,int aa):x(xx),y(yy),time(tt),a(aa){}
};
queue Q;

void init(){
    memset(visit,-1,sizeof(visit));
    while(!Q.empty())  Q.pop();
    ans=INF;
    f=false;
}

void read(){
    cin>>m>>n>>v;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>G[i][j];
            if(G[i][j]=='@'){
               //visit[i][j]=t;
               Q.push(Node(i,j,0,v));
            }
            else if(G[i][j]=='+'){
                ex=i;
                ey=j;
            }
        }
    }
}

bool inside(int x,int y){
    return x>=1 && x<=m && y>=1 && y<=n;
}
void BFS(){
    while(!Q.empty()){
        Node cur=Q.front();
        Q.pop();
        int x1=cur.x;
        int y1=cur.y;
        int time1=cur.time;
        int a1=cur.a;

        if(x1==ex && y1==ey){
            f=true;
            if(ans>time1) ans=time1;
        }

        for(int i=0;i<4;i++){
            int xx=x1+go[i][0];
            int yy=y1+go[i][1];
            if( inside(xx,yy)){ //现在拥有的金币 > 下一个要走的上一次走过时候的金币
                    if(G[xx][yy]=='#'  && a1>=1 && visit[xx][yy]

 

你可能感兴趣的:(算法练习)