【kuangbin 带你飞】专题一 简单搜索 题解

  • POJ 1321 棋盘问题(DFS)
  • POJ 3984 迷宫问题(BFS)
  • POJ 2251 Dungeon Master(BFS)
  • POJ 3278 Catch That Cow(BFS)
  • POJ 1426 Find The Multiple(BFS)
  • POJ 3126 Prime Path(BFS)
  • POJ 3087 Shuffle'm Up(模拟)
  • POJ 3414 Pots(BFS)
  • FZU 2150 Fire Game(两点BFS)
  • UVA 11624 Fire!(两点BFS)
  • HDU 1241 Oil Deposits(DFS)
  • HDU 2612 Find a way (两点BFS)



POJ 1321 棋盘问题(DFS)

原题链接:https://vjudge.net/problem/POJ-1321

  • 题意: 中文题目就不翻译了。

  • 思路: 用 vis[i] 数组来标记第 i 列是否放有棋子,sum 表示已放入多少个棋子,ans 表示方案数,从第一行开始深搜,当放入的棋子数等于给定需要放入的棋子数时,方案数加 1 并返回。当所搜索行数超出棋盘,也返回。

  • 注意: 因为是多组输入,所以计数的变量在每次循环开始前要初始化为0。


Code(C++):

#include 
#include 
using namespace std;
int n,k;    //n表示棋盘行列,k表示要放入的棋子数
int vis[10];    //标记第i列是否已放有棋子
int ans;    //方案数
int sum;    //已放入的棋子数
char ch[10][10];
void dfs(int x){
    if(sum==k){ //当放入的棋子数等于给定需要放入的棋子数时,方案数加1
        ans++;
        return;
    }
    if(x>n) //当所搜索的行数大于棋盘时,直接返回
        return;
    for(int i=1;i<=n;i++){	//搜索每一列
        if(!vis[i] && ch[x][i]=='#'){
            vis[i]=1;
            sum++;
            dfs(x+1);   //回溯
            vis[i]=0;
            sum--;
        }
    }
    dfs(x+1);
}
int main(){
    while(cin>>n>>k){
        if(n==-1 && k==-1)
            break;
        ans=0,sum=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cin>>ch[i][j];
        dfs(1); //从第一行开始搜索
        cout<<ans<<endl;
    }
    return 0;
}


POJ 3984 迷宫问题(BFS)

原题链接:https://vjudge.net/problem/POJ-3984

  • 题意: 给一个迷宫地图,1 表示墙壁,0 表示通道,只能横着走或者竖着走,求从左上角到右上角的最短路径。

  • 思路: 求最短路径一般就是广度优先搜索,定义结构体pre[x][y],表示该点的前一个元素,即保存路径。从左上角(0, 0)位置开始搜索,分别向上下左右搜索,已走过的点标记为1,未走过且可以走的点压入队列,最后当搜索到终点的时候,再把队列里的路径从头到尾一个个递归输出。


C++ Code:

#include 
#include 
#define ll long long
using namespace std;
const int N=1e6+5;
int cnt;
int vis[10][10],maps[10][10];
int dir_x[4]={0,0,1,-1};
int dir_y[4]={1,-1,0,0};
struct node{
    int x,y;
};
node pre[10][10];
void out(node cur){
    if(cur.x==0 && cur.y==0){
        cout<<"("<<cur.x<<", "<<cur.y<<")"<<endl;
        return;
    }
    out(pre[cur.x][cur.y]);
    cout<<"("<<cur.x<<", "<<cur.y<<")"<<endl;
}
void bfs(){
    queue<node> q;
    node start;
    start.x=0,start.y=0;
    q.push(start);
    vis[0][0]=1;
    while(!q.empty()){
        node now = q.front();
        q.pop();
        if(now.x==4 && now.y==4){
            out(now);
            return;
        }
        for(int i=0;i<4;i++){
            node next;
            next.x=now.x+dir_x[i];
            next.y=now.y+dir_y[i];
            if(next.x>=0 && next.x<5 && next.y>=0 && next.y<5 && !vis[next.x][next.y] && !maps[next.x][next.y]){
                vis[next.x][next.y]=1;
                q.push(next);
                pre[next.x][next.y]=now;
            }
        }
    }
}
int main(){
    for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)
            cin>>maps[i][j];
    bfs();
	return 0;
}

Java Code:

import java.util.Scanner;
class node{
	int x,y,pre;
	public node() {}
	public node(int x,int y,int pre) {
		this.x=x;
		this.y=y;
		this.pre=pre;
	}
}
public class Main {
	static int front=0,rear=1;
	static int[][] dir = {{1,0},{-1,0},{0,1},{0,-1}};
	static int[][] maps = new int[10][10];
	static node[] q = new node[100];
	static void print(int index) {
		if(q[index].pre!=-1) {
			print(q[index].pre);
			System.out.println("("+q[index].x+", "+q[index].y+")");
		}
	}
	static void bfs(int x,int y) {
		q[front]= new node(x,y,-1);
		while(front<rear) {
			for(int i=0;i<4;i++) {
				for(int j=0;j<4;j++) {
					int xx=q[front].x+dir[i][0];
					int yy=q[front].y+dir[i][1];
					if(xx<0||xx>4||yy<0||yy>4||maps[xx][yy]!=0)
						continue;
					maps[xx][yy]=1;
					q[rear] = new node(xx,yy,front);
					if(xx==4&&yy==4)	print(rear);
					rear++;
				}
			}
			front++;
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		for(int i=0;i<5;i++)
			for(int j=0;j<5;j++)
				maps[i][j]=cin.nextInt();
		for(int i=0;i<100;i++)	//结构体要全部初始化,不然搜索时会报空指针异常
			q[i]=new node();
		System.out.println("(0, 0)");
		bfs(0,0);
	}
}


POJ 2251 Dungeon Master(BFS)

原题链接:https://vjudge.net/problem/POJ-2251

  • 题意: 一个三维的迷宫题目,其中用‘.’表示空地,‘#’表示障碍物,‘S’表示起点,‘E’表示终点,可以上下左右前后移动,每次都只能移到相邻的空位,每次需要花费一分钟,求从起点到终点最少要多久。

  • 思路: 做法跟迷宫问题差不多,就是从二维变为三维。


C++ Code:

#include 
#include 
#include 
using namespace std;
int l,r,c;
char ch[50][50][50];
int vis[50][50][50];
int dir[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
struct node{
    int x,y,z,step;
};
void bfs(int sl,int sr,int sc,int el,int er,int ec){
    memset(vis,0,sizeof(vis));
    queue<node> q;
    node start;
    start.x=sl, start.y=sr, start.z=sc, start.step=0;
    vis[sl][sr][sc]=1;
    q.push(start);
    int flag=0;
    while(!q.empty()){
        node now = q.front();
        q.pop();
        if(now.x==el && now.y==er && now.z==ec){
            flag=1;
            cout<<"Escaped in "<<now.step<<" minute(s)."<<endl;
            return;
        }
        for(int i=0;i<6;i++){
            node next;
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            next.z=now.z+dir[i][2];
            next.step=now.step+1;
            if(next.x>=1 && next.x<=l && next.y>=1 && next.y<=r && next.z>=1 && next.z<=c && !vis[next.x][next.y][next.z] && ch[next.x][next.y][next.z]=='.'){
                vis[next.x][next.y][next.z]=1;
                q.push(next);
            }
        }
    }
    if(!flag)   cout<<"Trapped!"<<endl;
}
int main(){
    while(cin>>l>>r>>c && l && r && c){
        for(int i=1;i<=l;i++)
            for(int j=1;j<=r;j++)
                for(int k=1;k<=c;k++)
                    cin>>ch[i][j][k];
        int sl,sr,sc,el,er,ec;
        for(int i=1;i<=l;i++){
            for(int j=1;j<=r;j++){
                for(int k=1;k<=c;k++){
                    if(ch[i][j][k]=='S'){   //记录入口
                        sl=i;
                        sr=j;
                        sc=k;
                    }else if(ch[i][j][k]=='E'){ //记录出口
                        el=i;
                        er=j;
                        ec=k;
                        ch[i][j][k]='.';    //出口也是能走的,记得转换
                    }
                }
            }
        }
        bfs(sl,sr,sc,el,er,ec);
    }
    return 0;
}

Java Code:

import java.util.Scanner;
class node{
	int x,y,z,step;
	public node() {}
	public node(int x,int y,int z,int step) {
		this.x=x;
		this.y=y;
		this.z=z;
		this.step=step;
	}
}
public class Main {
	static int l,r,c,sx,sy,sz;
	static int front,rear;
	static boolean flag;
	static boolean[][][] vis = new boolean[50][50][50];
	static char[][][] maps = new char[50][50][50];
	static int[][] dir = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
	static node[] q = new node[300000];
	static void bfs(int sx,int sy,int sz) {
		front=1;	rear=2;
		vis[sx][sy][sz]=true;
		q[front]=new node(sx,sy,sz,0);
		while(front<rear) {
			int x = q[front].x;
			int y = q[front].y;
			int z = q[front].z;
			if(maps[x][y][z]=='E') {
				flag=true;
				System.out.println("Escaped in " + q[front].step + " minute(s).");
				return;
			}
			for(int i=0;i<6;i++) {
				int xx = x+dir[i][0];
				int yy = y+dir[i][1];
				int zz = z+dir[i][2];
				if(xx>=1 && xx<=l && yy>=1 && yy<=r && zz>=1 && zz<=c && maps[xx][yy][zz]!='#' && !vis[xx][yy][zz]) {
					vis[xx][yy][zz]=true;
					q[rear] = new node(xx,yy,zz,q[front].step+1);
					rear++;
				}	
			}
			front++;
		}
		if(!flag)	System.out.println("Trapped!");
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		while(cin.hasNext()) {
			flag=false;
			for(int i=0;i<50;i++)
				for(int j=0;j<50;j++)
					for(int k=0;k<50;k++)
						vis[i][j][k]=false;
			l=cin.nextInt();	r=cin.nextInt();	c=cin.nextInt();
			if(l==0 && r==0 && c==0)	break;
			for(int i=1;i<=l;i++)
				for(int j=1;j<=r;j++) {
					String str = cin.next();
					for(int k=1;k<=c;k++) {
						maps[i][j][k]=str.charAt(k-1);
						if(maps[i][j][k]=='S') {
							sx=i;	sy=j;	sz=k;
						}
					}
				}
			bfs(sx,sy,sz);	
		}
	}
}


POJ 3278 Catch That Cow(BFS)

原题链接: https://vjudge.net/problem/POJ-3278

  • 题意: 农夫追牛,牛不动,农夫有三种移动方式,第一种:向前移动一步;第二种:向后移动一步;第三种:农夫现在的位置乘以2;每次移动都消耗1分钟,求农夫追到牛的最短时间。

  • 思路: 求最短时间,可知要用bfs。从农夫的位置出发,分别把三种方法列出来,不断的广搜,同时更新所用的时间并存入数组,直到到达牛的位置k。


Code(C++):

#include 
#include 
using namespace std;
const int N=1e6;
int ans[N+10],vis[N+10];
queue<int> q;
void bfs(int n,int k){
    int pos=0;
    if(n==k)    return;
    q.push(n);
    while(!q.empty()){
        pos=q.front();
        q.pop();

        if(pos+1<=N && vis[pos+1]==0){  //向右移动一步
            ans[pos+1]=ans[pos]+1;
            vis[pos+1]=1;
            q.push(pos+1);
        }
        if(pos+1==k)    break;

        if(pos-1>=0 && vis[pos-1]==0){  //向左移动一步
            ans[pos-1]=ans[pos]+1;
            vis[pos-1]=1;
            q.push(pos-1);
        }
        if(pos-1==k)    break;

        if(pos*2<=N && vis[pos*2]==0){  //位置乘以2
            ans[pos*2]=ans[pos]+1;
            vis[pos*2]=1;
            q.push(pos*2);
        }
        if(pos*2==k)    break;
    }
}
int main(){
    int n,k;
    cin>>n>>k;
    bfs(n,k);
    cout<<ans[k]<<endl;
    return 0}


POJ 1426 Find The Multiple(BFS)

原题链接: https://vjudge.net/problem/POJ-1426

  • 题意: 输入一个200以内的正整数 n,求 n 的倍数 m 且要求 m 只由0、1组成,得到的答案的位数在100以内。符合条件的任意答案都可以。

  • 思路: 可以用STL的queue来做,不断地广搜n的倍数,一找到就输出。


Code(C++):

#include 
#include 
using namespace std;
typedef long long ll;
void bfs(int n){
    queue<ll> q;
    q.push(1);
    while(!q.empty()){
        ll x=q.front();
        q.pop();
        ll ans=x*10;
        if(ans%n==0){
            cout<<ans<<endl;
            return;
        }
        q.push(ans);
        ans=x*10+1;
        if(ans%n==0){
            cout<<ans<<endl;
            return;
        }
        q.push(ans);
    }
}
int main(){
    int n;
    while(cin>>n && n){
        bfs(n);
    }
    return 0;
}


POJ 3126 Prime Path(BFS)

原题链接: https://vjudge.net/problem/POJ-3126

  • 题意: 给定两个素数 n 和 m,要求把 n 变成 m,每次变换时只能变一个数字,即变换后的数与变换前的数只有一个数字不同,并且要保证变换后的四位数也是素数。求最小的变换次数。如果不能完成变换,输出Impossible。

  • 思路: 求最小变换次数,可知是用BFS,只要一找到就保证了变换次数最少。个位数是偶数的一定不是奇数,所以各位只能是奇数。千位数不能为0。所以对每个数位进行枚举,符合条件的话,就入队,并且标记已用过,直到变换成功。


Code(C++):

#include 
#include 
#include 
using namespace std;
int n,m;
int vis[10010];
struct node{
    int x;  //表示某数
    int step;   //表示路径数
};
queue<node> q;
bool isPrime(int x){    //判断是否为素数
    if(x==0||x==1)  return false;
    for(int i=2;i*i<=x;i++){
        if(x%i==0)  return false;
    }
    return true;

}
void bfs(){
    while(!q.empty()){
        node t=q.front();
        q.pop();
        int x0 = t.x;
        int step0 = t.step;
        if(x0==m){
            cout<<step0<<endl;
            return;
        }
        for(int i=1;i<=9;i+=2){ //个位只能是奇数
            int s=x0/10*10+i;
            if(!vis[s] && s!=x0 && isPrime(s)){
                vis[s]=1;
                node t;
                t.x=s;
                t.step=step0+1;
                q.push(t);
            }
        }
        for(int i=0;i<=9;i++){  //十位
            int s=x0/100*100+i*10+x0%10;
            if(!vis[s] && s!=x0 && isPrime(s)){
                vis[s]=1;
                node t;
                t.x=s;
                t.step=step0+1;
                q.push(t);
            }
        }
        for(int i=0;i<=9;i++){  //百位
            int s=x0/1000*1000+i*100+x0%100;
            if(!vis[s] && s!=x0 && isPrime(s)){
                vis[s]=1;
                node t;
                t.x=s;
                t.step=step0+1;
                q.push(t);
            }
        }
        for(int i=1;i<=9;i++){  //千位不能为0
            int s=i*1000+x0%1000;
            if(!vis[s]  && s!=x0 && isPrime(s)){
                vis[s]=1;
                node t;
                t.x=s;
                t.step=step0+1;
                q.push(t);
            }
        }
    }
    cout<<"Impossible"<<endl;
    return;
}
int main(){
    int t;  cin>>t;
    while(t--){
        while(!q.empty())   q.pop();
        cin>>n>>m;
        memset(vis,0,sizeof(vis));
        vis[n]=1;   //标记已经用过了
        node t;
        t.x=n,t.step=0;
        q.push(t);
        bfs();
    }
    return 0;
}


POJ 3087 Shuffle’m Up(模拟)

原题链接: https://vjudge.net/problem/POJ-3087

  • 题意: 已知两堆牌 s1 和 s2 的初始状态,其牌数均为c,按给定规则能将他们相互交叉组合成一堆牌 s12,再将 s12 的最底下的 c 块牌归为 s1,最顶的 c 块牌归为 s2,依此循环下去。给定输入 s1 和 s2 的初始状态以及预想的最终状态 s12。问 s1 和 s2 经过多少次洗牌之后,最终能达到状态 s12,若永远不可能相同,则输出 -1 。

  • 思路: 退化后的搜索,直接模拟。用 map 把目标状态的实值设为1,把出现过的字符串实值设为 2,洗牌后的字符串如果跟目标字符串相同,则输出洗牌次数。如果不相同,但是该字符串的实值却出现过,说明该状态出现过,也就是说明已经陷入了循环,则输出 -1。


Code(C++):

#include 
#include 
#include 
using namespace std;
int main(){
    int t;  cin>>t;
    for(int k=1;k<=t;k++){
        int n;  cin>>n;
        string s1,s2,s;
        cin>>s1>>s2>>s;
        map<string,int> ma;
        ma[s]=1;    //先把字符串s的键值设为1
        int ans=0;  //洗牌次数
        while(1){
            string s3="";   //洗牌后的字符串,要初始化为空字符
            for(int i=0;i<n;i++)    //交织洗牌
                s3=s3+s2[i]+s1[i];
            ans++;  //
            if(s==s3){  //如果洗牌后相同则输出
                cout<<k<<' '<<ans<<endl;
                break;
            }
            //洗牌后不相同,但是字符串却相同,说明出现过该状态,即进入了循环,也就是找不到了
            if(ma[s3]==1){  
                cout<<k<<' '<<-1<<endl;
                break;
            }
            ma[s3]=1;
            for(int i=0;i<n;i++){   //重新分配s1和s2
                s1[i]=s3[i];
                s2[i]=s3[i+n];
            }
        }
    }
    return 0;
}


POJ 3414 Pots(BFS)

原题链接: https://vjudge.net/problem/POJ-3414

  • 题意: 有两个水壶,对水壶有三种操作:

    1. FILL(i):将 i 水壶的水填满。

    2. DROP(i):将水壶 i 中的水全部倒掉。

    3. POUR(i, j):将水壶 i 中的水倒到水壶 j 中,若水壶 j 满了,则 i 剩下的就不倒了。

      问进行多少步操作,怎么操作可以使得两个水壶中至少有一个的水达到了 C 这个水量。可以的话,输出操作的步骤;不可能则输出impossible。注意初始时两个水壶是空的,没有水。

  • 思路: 两个壶一共有六种操作情况,使用BFS进行搜索,出现过的状态标记起来,具体见代码。


Code(C++):

#include 
#include 
#include 
using namespace std;
int a,b,c;
int ans;
int vis[105][105];  //标记该状态是否出现过
struct node{
    int x,y;    //代表两个杯子
    int step;   //操作数
    int flag;   //标记第几个步骤
    node *pre;  //前一步操作的路径
};
queue<node> q;
stack<int> s;
void bfs(){
    node t;
    node left[500]; //瓶子里剩余的水量
    t.x=0,t.y=0;
    t.flag=0,t.step=0;
    t.pre=NULL;
    q.push(t);
    vis[0][0]=1;
    int cnt=0;
    while(!q.empty()){
        left[++cnt]=q.front();
        q.pop();
        for(int i=1;i<=6;i++){
            switch(i){
                case 1: //从水龙头装满第一个水壶
                    t.x=a,t.y=left[cnt].y;
                    t.flag=1;
                    break;
                case 2: //从水龙头装满第二个水壶
                    t.x=left[cnt].x,t.y=b;
                    t.flag=2;
                    break;
                case 3: //将第一个水壶的水全部倒掉
                    t.x=0,t.y=left[cnt].y;
                    t.flag=3;
                    break;
                case 4: //将第二个水壶的水全部倒掉
                    t.x=left[cnt].x,t.y=0;
                    t.flag=4;
                    break;
                case 5: //将第一个水壶中的水倒入第二个水壶
                    if(left[cnt].x>b-left[cnt].y){  //如果第一个水壶所剩水比第二个水壶剩下空间大
                        t.x=left[cnt].x-(b-left[cnt].y);
                        t.y=b;
                    }else{
                        t.x=0;
                        t.y=left[cnt].y+left[cnt].x;
                    }
                    t.flag=5;
                    break;
                case 6: //将第二个水壶的水倒入第一个水壶
                    if(left[cnt].y>a-left[cnt].x){  //如果第二个水壶所剩水比第一个水壶剩下空间大
                        t.x=a;
                        t.y=left[cnt].y-(a-left[cnt].x);
                    }else{
                        t.x=left[cnt].x+left[cnt].y;
                        t.y=0;
                    }
                    t.flag=6;
                    break;
            }
            if(vis[t.x][t.y])   continue;
            vis[t.x][t.y]=1;
            t.step=left[cnt].step+1;
            t.pre=&left[cnt];
            if(t.x==c || t.y==c){   //一达到指定状态就把所有路径存入栈
                ans=t.step;
                while(t.pre){
                    s.push(t.flag);
                    t=*t.pre;
                }
                return;
            }
            q.push(t);
        }
    }
}
int main(){
    cin>>a>>b>>c;
    bfs();
    if(!ans) cout<<"impossible"<<endl;
    else{
        cout<<ans<<endl;
        while(!s.empty()){
            int k=s.top();
            s.pop();
            switch(k){
                case 1: cout<<"FILL(1)"<<endl;break;
                case 2: cout<<"FILL(2)"<<endl;break;
                case 3: cout<<"DROP(1)"<<endl;break;
                case 4: cout<<"DROP(2)"<<endl;break;
                case 5: cout<<"POUR(1,2)"<<endl;break;
                case 6: cout<<"POUR(2,1)"<<endl;break;

            }
        }
    }
    return 0;
}


FZU 2150 Fire Game(两点BFS)

原题链接: https://vjudge.net/problem/FZU-2150

  • 题意: 给一个 n*m 的地图,每个点有或者无草,可以同时在两个草坪点火,火势只能向上下左右蔓延,蔓延一次时间加1,求全部草烧完的最短燃烧时间。

  • 思路: 用一个结构体数组来存可以燃烧的点,然后两层循环同时暴力遍历这些可以燃烧的点,并且不断更新最短的燃烧的时间。

Code(C++):

#include 
#include 
#include 
using namespace std;
const int INF=0x3f3f3f3f;
int n,m;
int vis[15][15];
char ch[15][15];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};  //要么不用括号要么用大括号,一定不能用小括号
struct node{
    int x,y,step;
}arr[105];
int judge(int x,int y){
    if(x>=0&&x<n&&y>=0&&y<m&&!vis[x][y]&&ch[x][y]=='#')
        return 1;
    return 0;
}
int bfs(node a,node b){
    queue<node> q;
    q.push(a);
    q.push(b);
    vis[a.x][a.y]=1;
    vis[b.x][b.y]=1;
    int cnt=0;
    while(!q.empty()){
        a=q.front();
        cnt=max(cnt,a.step);
        for(int i=0;i<4;i++){
            b.x=a.x+dir[i][0],b.y=a.y+dir[i][1],b.step=a.step+1;
            if(judge(b.x,b.y)){
                q.push(b);
                vis[b.x][b.y]=1;
            }
        }
        q.pop();
    }
    return cnt;
}
int main(){
    int t;  cin>>t;
    for(int x=1;x<=t;x++){
        cin>>n>>m;
        int cnt=0;
        for(int i=0;i<n;i++){
            cin>>ch[i];
            for(int j=0;j<m;j++){
                if(ch[i][j]=='#'){
                    arr[cnt].x=i,arr[cnt].y=j,arr[cnt].step=0;
                    cnt++;
                }
            }
        }
        if(cnt<=2)  cout<<"Case "<<x<<": 0"<<endl;
        else{
            int ans=INF;
            for(int i=0;i<cnt;i++){
                for(int j=i;j<cnt;j++){
                    memset(vis,0,sizeof(vis));
                    int sum=bfs(arr[i],arr[j]);
                    int flag=0;
                    for(int k=0;k<n;k++){
                        for(int l=0;l<m;l++){
                            if(ch[k][l]=='#'&&!vis[k][l]){
                                flag=1;
                                break;
                            }
                        }
                        if(flag)    break;
                    }
                    if(!flag)   ans=min(ans,sum);
                }
            }
            if(ans==INF)    cout<<"Case "<<x<<": -1"<<endl;
            else    cout<<"Case "<<x<<": "<<ans<<endl;
        }
    }
    return 0;
}


UVA 11624 Fire!(两点BFS)

原题链接: https://vjudge.net/problem/UVA-11624

  • 题意: 给了很多个火(F)和一个人(J),火和人想四面蔓延的速度都是1,对于 J 来说,要是他走到某点的时间比火蔓延到该点的时间要短,那么他走到该点的时候,火还没蔓延过来,他就可以走到该点,否则,不能进入该点。问人能否走出迷宫?

  • 思路: 两点BFS的题目。先给地图外围加一圈点来判断是否走出迷宫,然后把人的位置和火焰的位置都记录下来,先让火焰入队,最后让人入队(因为先让火不断的烧,看看都可以烧到哪些地方以及相应的所需时间,这样人在逃跑的时候只要在火到达之前赶到那个地方就没事),用两个标记数组来标记火和人是否有走过或者烧过,因为火可以烧人走过的。

Code(C++):

#include 
#include 
#include 
using namespace std;
int n,m,xx,yy;
char ma[1005][1005];    //地图
int vis[1005][1005];    //标记是否被火烧过
int vip[1005][1005];    //标记是否被人走过
int dir[4][2]={1,0,-1,0,0,1,0,-1};  //搜索方向
struct node{
    int x,y,step,flag;  //flag为0表示人,为1表示火
}a[1005];
int bfs(int num){
    node now,next;
    queue<node> q;
    for(int i=0;i<num;i++){ //火先入队,标记为火
        q.push(a[i]);
        vis[a[i].x][a[i].y]=1;
    }
    q.push(a[num]); //人再入队,并标记为人
    vip[a[num].x][a[num].y]=1;
    while(!q.empty()){
        now = q.front();
        q.pop();
        if(!now.flag && (now.x<1||now.y<1||now.x>n||now.y>m)) return now.step;  //走出迷宫时返回步数
        for(int i=0;i<4;i++){
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            next.step=now.step+1;
            next.flag=now.flag;
            //如果是人且没走过,或也没烧过时可以搜下去人的路径
            if(!next.flag && !vis[next.x][next.y] && !vip[next.x][next.y] && ma[next.x][next.y]=='.'){
                vip[next.x][next.y]=1;
                q.push(next);
            }
            //如果是火且还没烧过并且在范围内可以搜下去火的路径
            if(next.flag==1&&!vis[next.x][next.y]&&next.x>=1&&next.y>=1&&next.x<=n&&next.y<=m&&ma[next.x][next.y]=='.'){
                vis[next.x][next.y]=1;
                q.push(next);
            }
        }
    }
    return 0;
}
int main(){
    int t;  cin>>t;
    while(t--){
        int num=0;  //表示火和人的总数量
        memset(vis,0,sizeof(vis));
        memset(vip,0,sizeof(vip));
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cin>>ma[i][j];
                if(ma[i][j]=='F'){  //找到火焰
                    a[num].x=i,a[num].y=j;
                    a[num].flag=1,a[num].step=0;
                    num++;
                }
                if(ma[i][j]=='J')   //找到人,先暂存,要放在最后面
                    xx=i,yy=j;
            }
        }
        a[num].x=xx,a[num].y=yy;
        a[num].step=0,a[num].flag=0;
        for(int i=0;i<=n+1;i++){    //外边围一圈点
            ma[i][0]='.';
            ma[i][m+1]='.';
        }
        for(int j=0;j<=m+1;j++){
            ma[0][j]='.';
            ma[n+1][j]='.';
        }
        int ans=bfs(num);
        if(ans) cout<<ans<<endl;
        else    cout<<"IMPOSSIBLE"<<endl;
    }
    return 0;
}


HDU 1241 Oil Deposits(DFS)

原题链接: https://vjudge.net/problem/POJ-1426

  • 题意: @ 代表油田,* 代表没有油田,如果一个油田8个方向有油田,则它们属于同一块区域,求有多少个区域。

  • 思路: 经典深搜题目,一搜到油田,区域数就加一,同时向八个方向继续搜索,把同一区域的油田标记为不是油田。

  • 注意: 要吸收掉换行符。


Code(C++):

#include 
#include 
using namespace std;
int n,m;
char map[105][105];
int dir[8][2]={{1,-1},{1,0},{1,1},{0,-1},{0,1},{-1,-1},{-1,0},{-1,1}};
void dfs(int x,int y){
    if(map[x][y]=='@')  //搜索到的油田标记为不是油田,表示已搜过
        map[x][y]='*';
    for(int i=0;i<8;i++){   //向八个方向搜索
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(map[xx][yy]=='@'&&xx>=0&&xx<n&&yy>=0&&yy<m)
            dfs(xx,yy);
    }
}
int main(){
    while(cin>>n>>m && n){
        getchar();  //吸掉换行符
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)
                cin>>map[i][j];
            getchar();  //吸掉换行符
        }
        int ans=0;  //区域的个数
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(map[i][j]=='@'){ //如果是油田就进行深搜与其同一区域的油田
                    dfs(i,j);
                    ans++;  //同时区域数加1
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}


HDU 2612 Find a way (两点BFS)

原题链接: https://vjudge.net/problem/HDU-2612

  • 题意: 给一幅图,有墙,有KFC,有路。两个人要去KFC约会,有很多个KFC,没到达相邻的道路要花费11分钟,问两个人去同一间KFC总共走的最少时间。

  • 思路: 还是两点BFS的题目,不过这道比较简单。在两个人的起始位置开始bfs,也就是进行两次bfs,然后计算到 ‘@’ 总共花费的时间,不断更新最小的时间。


Code(C++):

#include 
#include 
#include 
using namespace std;
int n,m;
char ch[205][205];
int vis[205][205],sum[205][205];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
struct node{
    int x,y,step;
};
void bfs(int i,int j){
    node a,b;
    queue<node> q;
    a.x=i,a.y=j,a.step=0;
    q.push(a);
    while(!q.empty()){
        a=q.front();
        q.pop();
        b.step=a.step+1;
        for(int i=0;i<4;i++){
            b.x=a.x+dir[i][0];
            b.y=a.y+dir[i][1];
            if(ch[b.x][b.y]!='#' && b.x>=0 && b.x<n && b.y>=0 && b.y<m && !vis[b.x][b.y]){
                vis[b.x][b.y]=1;
                sum[b.x][b.y]+=b.step;  //记录每个点两个人所走的次数和
                q.push(b);
            }
        }
    }
}
int main(){
    while(cin>>n>>m){
        memset(sum,0,sizeof(sum));
        for(int i=0;i<n;i++)
            cin>>ch[i];
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(ch[i][j]=='Y' || ch[i][j]=='M'){
                    memset(vis,0,sizeof(vis));  //两个点,所以在这里标记
                    bfs(i,j);
                }
            }
        }
        int ans=10000000;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(ch[i][j]=='@' && sum[i][j]<=ans && sum[i][j]!=0)
                    ans=sum[i][j];
            }
        }
        cout<<ans*11<<endl; //每走一次花费11分钟
    }
    return 0;
}


你可能感兴趣的:(【kuangbin 带你飞】专题一 简单搜索 题解)