[kuangbin带你飞]专题一 简单搜索题解

棋盘问题

POJ-1321
思路:这个键盘问题可归为八皇后问题的翻版,而这道题是一个行的全排列枚举,意思是每一行都有机会且几率平等被选到,最终选出满足条件的K行,那么方案数加一。所以所以可以用回溯法来实现这个行全排列问题。
代码:

#include<cstdio>
#include<cstring>
using namespace std;

const int MAX_N = 10;

int n,k;

bool col[MAX_N];    ///用来判断某列是否已经有元素了

char maze[MAX_N][MAX_N];

int ans = 0;

void dfs(int base,int cnt)
{
	///2
    for(int i=0;i<n;i++){
        if(maze[base][i]=='#'&&col[i]==0){
            if(cnt==k-1){
                ans++;
                //return;   不能return 后面的值还没有dfs
            }
            else{
                col[i] = 1;
                ///3
                for(int l = base+1; n-l+1>=k-cnt; l++)
                    dfs(l,cnt+1);
                col[i] = 0;
            }
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&k)==2&&n+1){
        memset(col,0,sizeof(col));
        memset(maze,0,sizeof(maze));
        ans = 0;
        char str[10];
        for(int i=0;i<n;i++){
            scanf("%s",str);
            for(int j=0;j<n;j++){
                maze[i][j] = str[j];
            }
        }
        ///1
        for(int i=0;i<n-k+1;i++){
            dfs(i,0);   ///代表dfs初始行与已填数目
        }
        printf("%d\n",ans);
    }
    return 0;
}

用回溯法实现行的全排列选k个,那就要注意代码的三个循环(代码里有注释分别对应1,2,3):对于循环1,枚举前面n-k+1列的行,而后面的行没有机会填满k个元素。然后循环2,类似八皇后的做法,对每一列进行枚举,情况满足的时候就递归。这里注意,满足递归条件不代表dfs可以return出,后面的元素同样有可能满足条件,所以要跑完整个循环。循环三当前一列满足条件,应当枚举的是下面的行,而下面的行每一个机会都相等,所以base下的每一行都要枚举出来,这样就实现了行的全排列枚举。
思想:对于回溯法应该有新的认识。回溯法服务于最开始的根节点,用于寻找满足条件或路径最长的dfs路,不管怎么搜索,根节点都是最初dfs的根节点,一直为其服务。而这道题可以看出,这道题的行(根节点)不应该只是第一行,而应该是1,2,…n-k+1行。同时还需注意要实现全排列,dfs里面的枚举应该也需循环,对于base下方的每一行都要枚举出来。

Dungeon Master

POJ-2251
这道题将搜索扩展成三维迷宫。实际考的:bfs路径记录。
思路:将vis开成三维空间,然后上bfs求最短路。bfs分为五个方向,东南西北以及下方。注意往下走一层也是step+1,所以这五个方向完全平等。循环五次即可。注意判断。
代码:

#include<stdio.h>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
char map[30][30][30];        //记录节点信息
int sta[30][30][30];        //标记是否访问
int base[6][3] = { {-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1} };
int L, R, C;
struct Piont
{
    int x, y, z;            //位置坐标
    int step;                //出发点到该点的步数
};
struct Piont s;                //起点
struct Piont e;                //终点
struct Piont curp;            //跳出循环时的节点

/******************判断是否到达终点*********************/
bool success(struct Piont cur)
{
    if (cur.x == e.x && cur.y == e.y && cur.z == e.z)
        return true;
    else
        return false;
}

/**************判断该点是否合法*************************/
bool check(int x, int y, int z)
{
    if ((x >= 0) && (x < L) && (y >= 0) && (y < R) && (z >= 0) && (z < C) && (!sta[x][y][z]) && (map[x][y][z] == '.' || map[x][y][z] == 'E'))
        return true;
    else
        return false;
}

///广搜
void bfs()
{
    struct Piont next;
    queue<Piont>q;
    q.push(s);
    //int flag = 0;
    while (!q.empty())
    {
        curp = q.front();
        q.pop();
        if (success(curp))
            return;
        else
        {
            sta[curp.x][curp.y][curp.z] = 1;
            for (int i = 0; i < 6; i++)
            {
                next.x = curp.x + base[i][0];
                next.y = curp.y + base[i][1];
                next.z = curp.z + base[i][2];
                if (check(next.x, next.y, next.z))        //扩展队列
                {
                    next.step = curp.step + 1;
                    sta[next.x][next.y][next.z] = 1;
                    q.push(next);
                }
             }
        }
    }
}
int main()
{
    while (scanf("%d%d%d", &L, &R, &C))
    {
        if((L == 0) && (R == 0) && (C == 0))
            break;
        memset(sta, 0, sizeof(sta));
        for (int i = 0; i < L; i++) {
            getchar();
            for (int j = 0; j < R; j++) {
                for (int k = 0; k < C; k++)
                {
                    scanf("%c", &map[i][j][k]);
                    if (map[i][j][k] == 'S') {
                        s.x = i;
                        s.y = j;
                        s.z = k;
                        s.step = 0;
                    }
                    else if (map[i][j][k] == 'E')
                    {
                        e.x = i;
                        e.y = j;
                        e.z = k;
                    }
                }
                getchar();
            }
        }
        bfs();
        if (curp.x == e.x && curp.y == e.y && curp.z == e.z)
            printf("Escaped in %d minute(s).\n", curp.step);
        else
            printf("Trapped!\n");
    }
    return 0;
}

这里再贴上bfs记录路径的伪代码:

bfs
q.push();	///将一个节点push进来,last=1;
int last=1,cur=0	///cur为下一路径的点的总数
while(!q.empty()){
	q.pop();
	last--;
	...bfs一系列操作,满足条件的点放入队列时cnt++
	if(last==0){
		last = cur;	///将这个路径的点继承给last,cur清零继续记录下一路径的点的个数
		step++;
		cur = 0;
	}
}
return;

Catch That Cow

POJ-3278
一道bfs记录路径长度的题目。一维的。难度没有上一题难。注意边界处理

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
 
const int maxn=100001;
 
bool vis[maxn];//标记数组
int step[maxn];//记录到了每一位置所走的步数
queue <int> q;//定义队列
 
int bfs(int n,int k)
{
    int head,next;
    q.push(n);   //开始FJ在n位置,n入队
    step[n]=0;
    vis[n]=true; //标记已访问
    while(!q.empty())  //当队列非空
    {
        head=q.front();  //取队首
        q.pop();         //弹出对首
        for(int i=0;i<3;i++)     //FJ的三种走法
        {
            if(i==0) next=head-1;
            else if(i==1) next=head+1;
            else next=head*2;
            if(next<0 || next>=maxn) continue; //排除出界情况
            if(!vis[next])  //如果next位置未被访问
            {
                q.push(next);    //入队
                step[next]=step[head]+1;  //步数+1
                vis[next]=true;  //标记已访问
            }
            if(next==k) return step[next];  //当遍历到结果,返回步数
        }
    }
}
int main()
{
    int n,k;
    while(cin>>n>>k)
    {
        memset(step,0,sizeof(step));
        memset(vis,false,sizeof(vis));
        
        while(!q.empty()) q.pop(); //注意调用前要先清空
        if(n>=k) printf("%d\n",n-k);
        else printf("%d\n",bfs(n,k));
    }
    return 0;
}

Fliptile

POJ-3279
其实是一道状态压缩题,不知道为什么归到了搜索上。是状压dp的一个衍生,要同时记录进行操作的opt数组。一个图可能没有答案,也可能有多个答案,字典序最小的输出。

思路:初始行数据是小于等于15的,容易想到二进制状态压缩dp,而这道题恰巧是这么做。对第一行二进制枚举操作点,每个操作点tap一次(自己及周围五个格翻转)那么对于第二行要操作的点就显而易见了,即都是在第一行还剩余1(黑格)的下方tap一次,因为这样就能保证第一行全为0(白格)。那么这样一直操作2~n行(我将N看成n行,输入不要错就行)最后检查最后一行是否全为0即可。
代码:

#include<cstring>
#include<memory>
#include<cstdio>
using namespace std;

const int MAX_N = 17;
const int inf = 0x3f3f3f3f;
int n,m;
int minn = inf;
int tap_cnt = 0;

int chess[MAX_N][MAX_N],cur[MAX_N][MAX_N];
int oper[MAX_N][MAX_N],ans[MAX_N][MAX_N];

void tap(int x,int y)
{
    cur[x][y]^=1,cur[x-1][y]^=1,cur[x+1][y]^=1,cur[x][y+1]^=1,cur[x][y-1]^=1;
}

bool ok()
{
    for(int i=1;i<=m;i++){
        if(oper[1][i]){
            tap(1,i);
            tap_cnt++;
        }
    }
    for(int i=2;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(cur[i-1][j]){
                tap(i,j);
                tap_cnt++;
                oper[i][j] = 1;
            }
        }
    }
    for(int i=1;i<=m;i++){
        if(cur[n][i]) return 0;
    }
    return 1;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&chess[i][j]);
        }
    }

    for(int i=0;i<(1<<m);i++){
        tap_cnt = 0;
        memset(oper,0,sizeof(oper)),memcpy(cur,chess,sizeof(cur));
        for(int j=m;j>=1;j--){
            oper[1][j] = (i>>(m-j))&1;
        }
        if(ok()&&tap_cnt>0&&tap_cnt<minn){
            minn = tap_cnt;
            memcpy(ans,oper,sizeof(ans));
        }
    }
    if(minn!=inf){
        for(int i=1;i<=n;i++){
            int flag = 1;
            for(int j=1;j<=m;j++){
                if(!flag) printf(" ");
                flag = 0;
                printf("%d",ans[i][j]);
            }
            printf("\n");
        }
    }
    else{
        printf("IMPOSSIBLE\n");
    }
    return 0;
}

Find The Multiple

POJ-1426
这个博客写的很好,下面代码仿照他的来写的。https://blog.csdn.net/qq_42964711/article/details/81938661
是一道双入口bfs+同余模的问题,好题。题目的注释在代码中写的比较清楚。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int mod[1000000] = {0};
int ans[1000000] = {0};
int n;

int main()
{
    while(scanf("%d",&n)==1&&n){
        memset(mod,0,sizeof(mod));
        mod[1] = 1%n;           ///1(2)
        int i;
        for(i=2;mod[i-1]!=0;i++){
            mod[i] = (mod[i/2]*10 + i%2)%n;
            ///mod[i/2]相当于对于二进制来说推了一位,
            ///举一反三,如果这道题只有0,1,2可以选,那么就是mod[i/3],
            ///相当于3进制中退了一位
            ///再用同余模定理处理一下
        }
        i--;    ///退一位,原本i-1已经符合条件了
        int pm = 0;
        while(i){
            ans[pm++] = i%2;
            i/=2;
        }
        while(pm){
            printf("%d",ans[--pm]);
        }
        printf("\n");
    }
    return 0;
}

Prime Path

POJ-3126
简单的bfs最短路题,直接上代码。
代码:

#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
struct Q
{
    int x[4];
    int s;
}no,de;
queue<Q> iq;
int n,a,b,ans,t[4];
int vir[10008];
 
bool isPrime( int x )
{
    for( int i=2; i<=sqrt(x); i++ )
    if( x%i == 0 )
    return false;
    return true;
}
 
int wirte( int* a)
{
    return a[0]*1000+a[1]*100+a[2]*10+a[3];
}
void bfs( )
{
    while( !iq.empty() )
    iq.pop();
    ans=0;
    memset(vir,0,sizeof(vir));
 
    no.s=0;
    for( int i=0; i<4; i++ )
    {
        no.x[3-i]=a%10;
        a/=10;
    }
    iq.push(no);
    vir[wirte(no.x)] = 1 ;
 
    while( !iq.empty() )
    {
        no = iq.front();
        iq.pop();
 
 
        if( wirte(no.x)==b )
        {
            printf("%d\n",no.s);
            return ;
        }
 
        for( int i=0; i<4; i++ )
        {
            de=no;
            for( int j=0; j<=9; j++ )
            {
                if( i==0 && j==0 ) continue;
                de.x[i] = j ;
                if( !vir[wirte(de.x)] && isPrime(wirte(de.x)))
                {
                    vir[wirte(de.x)] = 1 ;
                    de.s=no.s+1;
                    iq.push(de);
                }
            }
        }
    }
    printf("Impossible\n");
}
 
int main()
{
    scanf("%d",&n);
    while( n-- )
    {
        scanf("%d%d",&a,&b);
        bfs();
    }
    return 0;
}

Shuffle’m Up

POJ-3087

也不是搜索…是一道模拟题,模拟卡牌的叠放顺序。如果经过有限次模拟得到了与之前某个步骤相同的排法,说明无解,反之得到答案的话,就将答案输出。

#include<cstdio>
#include<cstring>
#include<set>
#include<string>
#include<iostream>
using namespace std;

set<string> S;

int main()
{
    int N;
    scanf("%d",&N);
    int kase = 0;
    char s1[405],s2[405],s12[805],fin[805];
    while(N--){
        int length;
        scanf("%d",&length);
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        memset(s12,0,sizeof(s12));
        memset(fin,0,sizeof(fin));
        S.clear();
        scanf("%s%s%s",s1,s2,fin);
        for(int i=0;i<length*2;i++){
            if(i&1){
                s12[i] = s1[i/2];
            }
            else{
                s12[i] = s2[i/2];
            }
        }
        S.insert(s12);
        bool flag = 0;
        int step = 1;
        char buf[805] = {0};
        while(1){
            //cout<<s12<<endl;
            if(strcmp(s12,fin)==0){
                flag = 1;
                break;
            }
            step++;
            //cout<<"l:"<<length<<endl;
            for(int i=0;i<length*2;i++){
                if((i&1)==0){
                    buf[i] = s12[i/2+length];
                }
                else{
                    buf[i] = s12[i/2];
                }
            }
            //cout<<"B:"<<buf<<'\n';
            if(S.count(buf)){
                break;
            }
            strcpy(s12,buf);
            S.insert(s12);
        }
        if(flag)
            printf("%d %d\n",++kase,step);
        else{
            printf("%d %d\n",++kase,-1);
        }
    }
    return 0;
}

Pots

POJ-3414
这是一道好题,是一个记录ans“路径名称”的一道题。下面的代码采用了链式前向星的思想将路径储存到op数组中,相信学过的同学应该都能看懂。但这道题不知道为何tle了,修改了几处也还是tle。如果哪位大牛看到这道题解,帮忙解释一下。
代码:

#include<cstdio>
#include<set>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;

const int MAX_N = 20000;    ///n(A) * n(B) 最多为10000种情况
const int MAX_V = 105;
int LA,LB,LC;

struct node
{
    int A;
    int B;
    int ind;
    node(int A=0,int B=0,int ind=0)
    :A(A),B(B),ind(ind)
    {}
};

struct OP
{
    int pre;
    string s1;///记录指向此点过程的字符串
    OP(int pre=0,string s1="\0")
    :pre(pre),s1(s1)
    {}
}op[MAX_N];

typedef pair<int,int> PII;

//set<PII> S;       用set导致TLE

int vis[MAX_V][MAX_V] = {0};
queue<node> q;

int step = 0;
int index = 0;

bool bfs(int a,int b)
{
    int id = 0;
    vis[a][b] = 1;
    op[id] = OP(-1,"0");///根本不会访问到
    q.push(node(a,b,id++));
    int cur = 0,last = 1;
    while(!q.empty()){
        node SE = q.front();
        q.pop();
        last--;
        int SA = SE.A,SB = SE.B,ID = SE.ind;
        //printf("test:%d %d\n",SA,SB);
        if(SA==LC||SB==LC){
            index = ID;
            return true;
        }
        for(int i=0;i<6;i++){
            int buf_L1 = SA,buf_L2 = SB;
            if(i==0){
            ///0~2为对A操作
                buf_L1 = LA;
                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"FILL(1)");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
            else if(i==1){
                buf_L1 = 0;
                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"DROP(1)");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
            else if(i==2){
                //printf("arr1:%d %d\n",buf_L1,buf_L2);
                if(buf_L2+buf_L1<=LB){
                    buf_L2 += buf_L1;///反过来,太tmd隐蔽了
                    buf_L1 = 0;///
                }
                else{
                    buf_L1 -= LB - buf_L2;
                    buf_L2 = LB;
                }
                //printf("arr2:%d %d\n",buf_L1,buf_L2);

                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"POUR(1,2)");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
            ///3~5为对B操作
            else if(i==3){
                buf_L2 = LB;
                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"FILL(2)");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
            else if(i==4){
                buf_L2 = 0;
                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"DROP(2)");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
            else{
                if(buf_L1+buf_L2<=LA){
                    buf_L1 += buf_L2;
                    buf_L2 = 0;
                }
                else{
                    buf_L2 -= LA - buf_L1;
                    buf_L1 = LA;
                }
                if(!vis[buf_L1][buf_L2]){
                    vis[buf_L1][buf_L2] = 1;
                    op[id] = OP(ID,"POUR(2,1)");
                    //printf("arr\n");
                    q.push(node(buf_L1,buf_L2,id++));
                    cur++;
                }
            }
        }
        if(last==0){
            step++;
            last = cur;
            cur = 0;
        }
    }
    return false;
}



void ans_print()
{
    vector<string> vec;
    printf("%d\n",step);
    int p = index;
    while(p!=0){
        vec.push_back(op[p].s1);
        p = op[p].pre;
    }
    for(int i=vec.size()-1;i>=0;i--){
        printf("%s\n",vec[i].c_str());
    }
}

int main()
{
    scanf("%d%d%d",&LA,&LB,&LC);
    if(bfs(0,0)){
        ans_print();
    }
    else{
        printf("impossible\n");
    }
    return 0;
}

Fire Game

FZU-2150
这道题,好题!
首先是bfs(或不回溯dfs)找连通块个数,这题要写一份。
其次是找一图的一个点,这个点能以最短的时间扩散完整个图,那么这个最短的时间是多少?可以结合图论知识,其实就是找树的最大直径除以二,得到的就是最短时间。把这个连通块想成一颗树,随便选取连通块的一点bfs,找到距离这点最远的点P,再从P点bfs一次,到最远的点的距离就是树的直径,(1
+m(p点距离))/2就是答案。
但一图找两个点扩散,就只能枚举连通块的点啦。
以下就是一点扩散没优化过的代码。
代码:

#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
char map[150][150];
int vis[150][150];
int cnt,n,m,ans;
const int inf=0x3f3f3f3f;
struct node{
	int x;
	int y;
	int step;
};
bool check(int x,int y){
	if(x<0||x>=n||y<0||y>=m||vis[x][y]==1||map[x][y]!='#') return false;
	return true;
}
bool checkAll(){
	for(int i=0;i<n;i++)
	   for(int j=0;j<m;j++){
	   	if(map[i][j]=='#'&&vis[i][j]==0) return false;
	   } 
	   return true;
}
int bfs(node a,node b){
	queue<node> que;
	memset(vis,0,sizeof(vis));
	que.push(a);
	que.push(b);
	int steps=0;
 
	while(!que.empty()){
		node p=que.front();
		que.pop();
		if(vis[p.x][p.y]) continue;
		 vis[p.x][p.y]=1;
		 steps=p.step;
		 if(check(p.x+1,p.y)){
		 	node next=p;
		 	next.step++;
		 	next.x++;
		 	que.push(next);
		 }
		 if(check(p.x,p.y+1)){
		 	node next=p;
		 	next.step++;
		 	next.y++;
		 	que.push(next);
		 }
		 if(check(p.x-1,p.y)){
		 	node next=p;
		 	next.step++;
		 	next.x--;
		 	que.push(next);
		 }
		 if(check(p.x,p.y-1)){
		 	node next=p;
		 	next.step++;
		 	next.y--;
		 	que.push(next);
		 }
	}
	return steps;
}
int main(){
	int ans=inf;
	int t,kase=1;
	cin>>t;
	vector<node> collect;
	while(t--){
		ans=inf;
		scanf("%d%d",&n,&m);
		memset(vis,0,sizeof(vis));
		collect.clear();
		for(int i=0;i<n;i++)
			scanf("%s",&map[i]);
			
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++){
				if(map[i][j]=='#'){
					node c;
					c.x=i;
					c.y=j;
					c.step=0;
					collect.push_back(c);
				}
			}
		for(int i=0;i<collect.size();i++)
			for(int j=i;j<collect.size();j++){
				collect[i].step=collect[j].step=0;
				int aos=bfs(collect[i],collect[j]);
				if(checkAll()) ans=min(ans,aos);
			}
			if(ans==inf) printf("Case %d: %d\n",kase++,-1);
			else printf("Case %d: %d\n",kase++,ans);
	}
	
}

Fire!

UVA-11624
相同速度的同步双bfs。人向四处搜索的速度与火向四周蔓延的速度一样,所以要记录每个火焰和人扩散的每个点的步数。好题,同步双bfs(一个bfs写下)的写法如下。
注意,两个bfs的地位是平等的,所以应该一个bfs上完后,就上另外一个bfs。
另外开始可能有多个火焰口,要注意
代码:

#include<bits/stdc++.h>
using namespace std;

int R,C;

const int MAX_N = 1005;
const int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};

char maze[MAX_N][MAX_N] = {0};
int vis[MAX_N][MAX_N] = {0};


struct node
{
    int x,y;
    node(int x=0,int y=0):x(x),y(y){}
};

node J,F[MAX_N*MAX_N];
int cnt_F = 0;

bool out(int x,int y)
{
    return x<1||x>R||y<1||y>C;
}

bool in(int x,int y)
{
    return 1<=x&&x<=R&&1<=y&&y<=C;
}

queue<node> J_q;
queue<node> F_q;

int kkkkase = 0;
int bfs(int tx,int ty)
{
    while(!J_q.empty()) J_q.pop();
    while(!F_q.empty()) F_q.pop();
    J_q.push(node(tx,ty));
    for(int i=0;i<cnt_F;i++)
        F_q.push(node(F[i].x,F[i].y));
    vis[tx][ty] = 1;
    int last = 1,cur = 0,step = 1;
    int last_f = cnt_F,cur_f = 0;
    bool flag = 1;
    while(1){
        #ifdef test
        cout<<++kkkkase<<":\n";
        for(int i=1;i<=R;i++){
            for(int j=1;j<=C;j++){
                cout<<vis[i][j]<<' ';
            }
            cout<<'\n';
        }
        #endif
        if(!flag) break;///说明人已经无路可走了
        while(!F_q.empty()){
            node FE = F_q.front();
            F_q.pop();
            last_f--;
            for(int i=0;i<4;i++){
                int ex = FE.x + dir[i][0],ey = FE.y + dir[i][1];
                if(in(ex,ey)&&!vis[ex][ey]){
                    vis[ex][ey] = 2;
                    F_q.push(node(ex,ey));
                    cur_f++;
                }
            }
            if(last_f==0){
                last_f = cur_f;
                cur_f = 0;
                break;///跳出这层火的循环,开始人的扩散
            }
        }
        while(1){
            if(J_q.empty()){
                flag = 0;
                break;
            }
            node PE = J_q.front();
            J_q.pop();
            last--;
            for(int i=0;i<4;i++){
                int ex = PE.x + dir[i][0],ey = PE.y + dir[i][1];
                if(out(ex,ey)){
                    return step;
                }
                if(in(ex,ey)&&!vis[ex][ey]){
                    vis[ex][ey] = step;
                    J_q.push(node(ex,ey));
                    cur++;
                }
            }
            if(last==0){
                last = cur;
                cur = 0;
                step++;
                break;///跳出人的bfs,进行下一轮step+1的循环
            }
        }
    }
    return -1;
}



int main()
{
    //ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;
    cin>>T;
    while(T--){
        cin>>R>>C;
        memset(maze,0,sizeof(maze));
        memset(vis,0,sizeof(vis));
        //memset(F,0,sizeof(F));
        cnt_F = 0;
        ///getchar()可能(在这道题就是)会导致RE
        ///队列出入一定要判断是否为空,不然会RE
        ///while(!q.empty()) pop是初始化的方法
        for(int i=1;i<=R;i++){
            char str[MAX_N] = {0};
            cin>>str;
            for(int j=1;j<=C;j++){
                maze[i][j] = str[j-1];
                if(maze[i][j]=='#') vis[i][j] = 1;
                else if(maze[i][j]=='J') J = node(i,j),vis[i][j] = 0;
                else if(maze[i][j]=='F') F[cnt_F++] = node(i,j),vis[i][j] = 2;
                else vis[i][j] = 0;
            }
        }

        int ans;
        if((ans=bfs(J.x,J.y))!=-1){
            cout<<ans<<'\n';
        }
        else{
            cout<<"IMPOSSIBLE"<<'\n';
        }
    }
    return 0;
}

迷宫问题

POJ-3984
如果上一道题明白透了,那这道题就不成问题。
bfs+路径储存,用链式前向星的思想完成就可以了。
代码:(可能代码不是链式前向星完成的,因为这道题很久之前就做过了,直接粘上代码)

#include<stdio.h>
#include<string.h>
#define max 10
int map[max][max];
int vis[max][max];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
struct node
{
    int x,y;
}queue[max*max];
int a[max*max];
int b[max*max];
int cnt;
int p[max*max];
void print(int k)
{
    int i;
    int t=p[k];
    if(t==0)
    {
        printf("(0, 0)\n");
        printf("(%d, %d)\n",a[k],b[k]);
    }
    else
    {
        print(t);
        printf("(%d, %d)\n",a[k],b[k]);
    }
}
void bfs(int x,int y)
{
    struct node now,pre;
    queue[0].x=0;
    queue[0].y=0;
    int e=0,h=1;
    p[0]=-1;
    while(e<h)
    {
        pre=queue[e];
        if(pre.x==4&&pre.y==4)
        {
            print(e);
            return;
        }
        int i;
        for(i=0;i<4;i++)
        {
            now.x=pre.x+dir[i][0];
            now.y=pre.y+dir[i][1];
            if(now.x>=0&&now.x<5&&now.y>=0&&now.y<5&&!vis[now.x][now.y]&&map[now.x][now.y]==0)
            {
                vis[now.x][now.y]=1;
                queue[h]=now;
                a[h]=now.x;
                b[h]=now.y;
                p[h]=e;
                h++;
            }
        }
        e++;
    }
    return;
}
int main()
{
    int i,j;
    cnt=0;
    for(i=0;i<5;i++)
    {
        for(j=0;j<5;j++)
        {
            scanf("%d",&map[i][j]);
        }
    }
    memset(vis,0,sizeof(vis));
    bfs(0,0);
    return 0;
}

							这里是分割线

题目尚且没有补完,仅先开此博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值