• 用BFS求最短路 - 习题6道


    可用BFS求解图中两个结点之间的最短路径。这样的图通常在形式上为矩形点阵(网格迷宫),每个可走的点(网格,下同)为图的结点,图的边则描述了从一个结点与其相邻结点直接连通的状态。在二叉树的BFS中,结点的访问顺序恰好是它们到根结点距离从小到大的顺序。类似地,图的BFS的过程就是把从起点到终点过程中遍历的点建成以起点为根结点,终点为叶节点的一棵树(称为最短路树,或者BFS树)的过程。遍历的顺序为到起点的距离。规定同一个结点只能被拓展到一次,从而保证父结点的唯一性和拓展路径最短。

    【提示】很多复杂的迷宫问题都可以转化为最短路问题,然后用BFS求解。在套用BFS框架之前,需要先搞清楚图中的“结点”包含哪些内容。

    Abbott的复仇(ACM/ICPC World Finals 2000)

    【题目】
    有一个最多包含9×9个交叉点的迷宫。输入起点、离开起点时的朝向和终点,求一条最短路(多解时任意输出一个即可)。
    这个迷宫的特殊之处在于:进入一个交叉点的方向不同,允许出去的方向也不同。
    注意:初始状态是“刚刚离开入口”,所以即使出口和入口重合,最短路也不为空。

    【分析】
    本题和普通的迷宫在本质上是一样的,但是由于“朝向”也起到了关键作用,所以需要用一个三元组(r, c, dir)表示“位于(r,c),面朝dir”这个状态。由于起点没有路牌,且指定了到下一个点唯一路径与朝向,假设入口位置为(r0, c0),朝向为dir,则初始状态并不是(r0, c0, dir),而是(r1, c1, dir),其中,(r1,c1)是(r0,c0)沿着方向dir走一步之后的坐标。
    首先是输入过程。将4个方向和3种“转弯方式”编号为0~3和0~2,并且提供相应的转换函数。输入函数比较简单,作用就是读取r0, c0, dir,并且计算出r1, c1,然后读入has_edge数组。
    接下来是“行走”函数,根据当前状态和转弯方式,计算出后继状态。
    下面是BFS主过程。
    最后是解的打印过程。它也可以写成递归函数,不过用vector保存结点可以避免递归时出现栈溢出,并且更加灵活。

    【提示】使用BFS求出图的最短路之后,可以用递归方式打印最短路的具体路径。如果最短路非常长,递归可能会引起栈溢出,此时可以改用循环,用vector保存路径。

    #include
    #include
    #include
    #include
    using namespace std;
    
    struct Node {
    	int r, c, dir; // 站在(r,c),面朝方向dir
    	Node(int r=0, int c=0, int dir=0):r(r),c(c),dir(dir) {}
    	//!结构体的递归定义 
    };
    
    const int maxn = 10;
    const char* dirs = "NESW"; // 顺时针旋转
    const char* turns = "FLR";
    
    int has_edge[maxn][maxn][4][3];
    int d[maxn][maxn][4];
    Node p[maxn][maxn][4];
    int r0, c0, dir, r1, c1, r2, c2;
    
    int dir_id(char c) { return strchr(dirs, c) - dirs; }
    //将代表朝向的4种字符转换成相应的编号 
    int turn_id(char c) { return strchr(turns, c) - turns; }
    //将代表转向的3种字符转换成相应的编号
    
    const int dr[] = {-1, 0, 1, 0};//4种走向对应的4种行坐标变化
    const int dc[] = {0, 1, 0, -1};//4种走向对应的4种列坐标变化
    
    Node walk(const Node& u, int turn){ //产生尝试拓展之后的结点 
    	int dir = u.dir;
    	if(turn == 1) dir = (dir + 3) % 4; // 逆时针
    	if(turn == 2) dir = (dir + 1) % 4; // 顺时针
    	return Node(u.r + dr[dir], u.c + dc[dir], dir);
    }
    
    bool inside(int r, int c) //判断当前坐标是否越界 
    {return r >= 1 && r <= 9 && c >= 1 && c <= 9;}
    
    bool read_case() {
    	char s[99], s2[99];
    	if(scanf("%s%d%d%s%d%d", s, &r0, &c0, s2, &r2, &c2) != 6)
    		return false;
    	printf("%s\n", s);
    	dir = dir_id(s2[0]);
    	r1 = r0 + dr[dir];
    	c1 = c0 + dc[dir];
    	
    	memset(has_edge, 0, sizeof(has_edge));
    	for(;;){
    		int r, c;
    		scanf("%d", &r);//r代表行 
    		if(r == 0) break;
    		scanf("%d", &c);//c代表列
    		while(scanf("%s", s) == 1 && s[0] != '*'){
    			for(int i = 1; i < strlen(s); i++)
    				has_edge[r][c][dir_id(s[0])][turn_id(s[i])] = 1;
    				//has_edge[r][c][dir][turn]表示当前状态是(r,c,dir),是否可以沿着转弯方向turn行走。 
    		}
    	}
    	return true;
    }
    
    void print_ans(Node u){
    	// 从目标结点逆序追溯到初始结点
    	vector<Node> nodes;
    	for(;;){
    		nodes.push_back(u);
    		if(d[u.r][u.c][u.dir] == 0) break;
    		u = p[u.r][u.c][u.dir];
    	}
    	nodes.push_back(Node(r0, c0, dir));
    	
    	// 打印解,每行10个
    	int cnt = 0;
    	for(int i = nodes.size()-1; i >= 0; i--) {
    		if(cnt % 10 == 0) printf(" ");
    		printf(" (%d,%d)", nodes[i].r, nodes[i].c);
    		if(++cnt % 10 == 0) printf("\n");
    	}
    	if(nodes.size() % 10 != 0) printf("\n");
    }
    
    void solve(){
    	queue<Node> q;
    	memset(d, -1, sizeof(d));//-1表示起点到该点的最短路尚未生成 
    	Node u(r1, c1, dir);//相当于"Node u={r1,c1,dir};"
    	d[u.r][u.c][u.dir] = 0;//d[r][c][dir]表示初始状态到(r,c,dir)的最短路长度
    	q.push(u);
    	while(!q.empty()) {
    		Node u = q.front(); q.pop();
    		if(u.r == r2 && u.c == c2) { print_ans(u); return; }
    		for(int i = 0; i < 3; i++){ //尝试向4个方向拓展 
    			Node v = walk(u, i);
    			if(has_edge[u.r][u.c][u.dir][i] && inside(v.r, v.c) && d[v.r][v.c][v.dir] < 0) {
    				d[v.r][v.c][v.dir] = d[u.r][u.c][u.dir] + 1;
    				p[v.r][v.c][v.dir] = u;
    				//用p[r][c][dir]保存状态(r,c,dir)在BFS树中的父结点
    				q.push(v);
    			}
    		}
    	}
    	printf("  No Solution Possible\n");
    }
    
    int main() {
    	while(read_case())
    		solve();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    巡逻机器人(ACM/ICPC Hanoi 2006)

    【题目】
    时间限制: 3 Sec 内存限制: 128 MB
     题目描述
    一个机器人从m×n的网格的左上角(1,1)走到右下角(m,n),每个格子是空地或者障碍,机器人每次可以向四个方向走一步,但是不能连续踩上k个障碍,求最短步数。(起点和终点都是空地)
     输入
    输入有多组。输入文件的第一行为组数,不大于20。 每组输入的第一行包含两个用空格分隔的正整数m和n (1≤m,n≤20),第二行包含一个整数k(0≤k≤20)。接下来m行,每行包含用空格分隔的n个整数,以值“1”代表障碍,“0”代表空地。(i = 1, 2, . . . , m; j = 1, 2, . . . , n)
     输出
    对于每组输入,如果机器人可以到达(m,n),则输出机器人需要移动的最少次数,否则输出“-1”。
     样例输入
    3
    2 5
    0
    0 1 0 0 0
    0 0 0 1 0
    4 6
    1
    0 1 1 0 0 0
    0 0 1 0 1 1
    0 1 1 1 1 0
    0 1 1 1 0 0
    2 2
    0
    0 1
    1 0
     样例输出
    7
    10
    -1

    【分析】

    变在哪里:
    普通BFS的结点只有两种情况,通或者不通,能走的路是通路,一个子节点只能属于一个父节点,因此必然存在一个树可以连通所有通路结点,从而求出最短路径。
    而本题可以跨越障碍,这样就产生了至少有三种状态:通,有障碍但是可以走(即在k范围内),不通。从树的角度来看,一个子节点可能属于多个父节点。(也就是说,走已经走过的点也可能是最优解,普通BFS走已经走过的点必然不是最优解)因此,不能单纯的连成一个二叉树,而是要连成一个图,在图中找最短路。
    在寻路过程中,每一步都受到过去的影响,而且影响未来。如果当前遇到障碍,过去已跨越的障碍数量决定了是否能够跨越当前障碍;当前是否跨过障碍,决定了未来能否跨过障碍。显然,走到当前位置跨越的障碍越少越好,因为之后可能还有障碍,这样就可以跨过更多的障碍。

    解题关键:
    在允许重复访问结点的同时,避免走回头路,仅保留跨越障碍最少的方案。

    解决方法:
    取消对结点“已访问”状态的标记,用一个数组存储到达当前结点最少需要跨越几个障碍。(由于BFS的特性,到同一个结点的步数必然是相同的,但是它跨过的障碍数可能不同)。走到一个点后,查表判断其是否更优,若不是,则舍弃。为什么这样可行?如果试图访问已走过的结点,再次访问时跨越障碍的数量一定不会比第一次少,会被舍弃;由于试探有顺序,可能在到达最优之前保存了不完美的方案,但最少障碍会更新,不完美方案最终会被淘汰。

    可行方案倒推:结点的状态增加了连续踩上障碍的数量,对于每个网格(不管有无障碍)还是只允许访问一次,因为对于一个目的网格其周围有多个相邻网格,走到这些相邻网格时连续踩上障碍的数量也不同,只要解存在,总可以产生,所以可行。

    #include
    #include
    #include
    using namespace std;
    const int N=20+5;
    const int inf=N*N;
    struct node
    	{int r,c,o;};//r为行标,c为列标,o为连续踩上障碍数
    int m,n,k,map[N][N];
    void read_case(){
    	cin>>m>>n;//m为行数,n为列数
    	cin>>k;//k为连续踩上障碍上限
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<=n;j++)
    			cin>>map[i][j];
    }
    int d[N][N][N];
    const int dr[] = {1, 0, -1, 0};
    const int dc[] = {0, 1, 0, -1};
    bool inside(int r, int c) 
    {return r>=1&&r<=m&&c>=1&&c<=n;}
    node walk(const node& u, int i){
    	int r=u.r+dr[i],c=u.c+dc[i],o=u.o;
    	if(inside(r,c)){
    		if(map[r][c]) o++;else o=0;
    		return {r,c,o};
    	}	
    	else return {0,0,inf};
    }
    void solve(){
    	queue<node>q;
    	memset(d, -1, sizeof(d));//-1表示起点到该点的最短路尚未生成
    	d[1][1][0] = 0;//d[r][c][o]表示初始状态到(r,c,o)的最短路长度
    	node u={1,1,0};q.push(u);
    	int ans=-1;
    	while(!q.empty()){
    		node u=q.front();q.pop();
    		if(u.r==m&&u.c==n) {ans=d[m][n][u.o];break;} 
    		for(int i=0;i<4;i++){
    			node v=walk(u,i);
    			if(v.o<=k&&d[v.r][v.c][v.o]<0){
    				d[v.r][v.c][v.o]=d[u.r][u.c][u.o]+1;
    				q.push(v);
    			}
    		}
    	}
    	cout<<ans<<endl;
    }
    int main(){
    	int ca;cin>>ca;
    	while(ca--)
    		{read_case();solve();}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    HDOJ-1548 A strange lift

    【题目】
    有一个奇怪的大楼和奇怪的电梯,大楼每一层的地上都标着一个数值。电梯一次只能移动(上升或下降)指定层数,就是移动前所在楼层地上的数值。输入起始楼层A、楼层数N和目的楼层B,输出最少移动次数。(电梯最高可到达N层,最低可到达1层)(如果无解,输出-1)

    【思路】

    广度优先搜索思考如下问题:
    队列存储内容?
    每次扩展方向及数值?
    越界判断?

    流程:
    每个结点步数初始化为无穷大
    起点加入队首,步数设为0
    循环处理队列:取队首,终点判断,向各个方向扩展,可扩展者入队并更新步数,队首出队
    判断终点步数是否被缩短过

    #include
    #include
    using namespace std;
    int main(){
    	int n;
    	while(cin>>n&&n){
    		int A,B,i,fr;
    		int k[205]={0};//k存储各楼层对应的上下值
    		int s[205]={0};//s存储走到该楼层所用步数 
    		cin>>A>>B;
    		for(i=1;i<=n;i++)
    			{cin>>k[i];s[i]=205;}//s初始化为无穷大
    		queue<int>q;//队列存储走到的楼层 
    		q.push(A);s[A]=0;//从A出发,入队
    		while(!q.empty()){//队列非空 
    			fr=q.front();//取队首存入fr			
    			if(fr==B) break;//到达终点
    			if(fr+k[fr]<=n&&s[fr+k[fr]]>s[fr]+1)//fr+k[fr]未越界且未走到过 
    				{q.push(fr+k[fr]);s[fr+k[fr]]=s[fr]+1;}//向上扩展新结点 
    			if(fr-k[fr]>=1&&s[fr-k[fr]]>s[fr]+1)//向下扩展新结点 
    				{q.push(fr-k[fr]);s[fr-k[fr]]=s[fr]+1;}
    			q.pop();//队首出列
    		}
    		if(s[B]!=205) cout<<s[B]<<endl;
    		else cout<<"-1\n";
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    POJ-2251 Dungeon Master

    【题目】

    你困在一个3D地牢中,每个单元可走(普通.,起点S,终点E)或不可走(#),只能向东南西北上下移动。走一格要花一分钟,分层输入地牢地图,输出最短逃逸时间。

    #include
    #include
    using namespace std;
    struct node
    	{int z;int x;int y;int t;}fr;//z代表层数,t代表步数
    queue<node>q;
    char map[33][33][33];int l,r,c,i,j,k;
    void finds()
    {
    	for(i=0;i<l;i++)
    		for(j=0;j<r;j++)
    			for(k=0;k<c;k++)
    				if(map[i][j][k]=='S')
    					{q.push({i,j,k,0});map[i][j][k]='#';return;}
    }
    int dir[6][3]={{0,1,0},{0,-1,0},{1,0,0},{-1,0,0},{0,0,1},{0,0,-1}};//方向数组
    int finde(){
    	int nx,ny,nz,mt;
    	while(!q.empty()){
    		fr=q.front();
    		for(i=0;i<6;i++){
    			nx=fr.x+dir[i][0];ny=fr.y+dir[i][1];nz=fr.z+dir[i][2];
    			if(nx<0||nx==r||ny<0||ny==c||nz<0||nz==l) continue;
    			switch(map[nz][nx][ny])//对下一结点进行处理
    			{
    				case '#':break;
    				case '.':q.push({nz,nx,ny,fr.t+1});map[nz][nx][ny]='#';break;
    				case 'E':return fr.t+1;
    			}
    		}
    		q.pop();
    	}
    	return 0;
    }
    int main()
    {	
    	while(scanf("%d%d%d",&l,&r,&c)&&l&&r&&c){
    		for(i=0;i<l;i++){
    			getchar();
    			for(j=0;j<r;j++)
    				gets(map[i][j]);
    		}
    		getchar();
    		while(!q.empty()) q.pop();
    		finds();
    		int ans=finde();
    		if(ans>0) printf("Escaped in %d minute(s).\n",ans);
    		else printf("Trapped!\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    HDOJ-2102 A计划

    【题目】
    公主被关在一个两层的迷宫里,骑士需要在T时刻或之前找到公主。迷宫的入口是S(0,0,0),公主的位置用P表示,时空传输机用#表示,墙用*表示,平地用.表示。骑士们一进入时空传输机就会被转到另一层的相对位置,但如果被转到的位置是墙的话,那骑士们就会被撞死。骑士们在一层中只能上下左右移动,每移动一格花1时刻。层间的移动只能通过时空传输机,且不需要任何时间。输入地图,判断骑士能否完成任务。

    【思路】
    地图的两层之间有联系,故用三维数组存储。
    多分支选择处理迷宫格的值,遇到时空传输机则需嵌套一个多分支选择处理另一层迷宫格的值。
    访问过的点标记为墙。
    时空传输机对应墙的标为墙(所有层)。两个时空传输机相对会死循环,也标为墙。

    #include
    #include
    using namespace std;
    struct node
    	{int x;int y;int z;int t;}fr,temp;//z代表层数,t代表步数
    int cs(int z)
    	{if(z==0) return 1;
    	return 0;}
    int main(){
    	int c;
    	char map[2][15][15];//三维数组
    	int dir[4][2]={0,1,0,-1,1,0,-1,0};//方向数组
    	scanf("%d",&c);
    	while(c--){
    		int n,m,T,i;
    		scanf("%d%d%d",&n,&m,&T);
    		getchar();
    		for(i=0;i<n;i++) gets(map[0][i]);
    		getchar();//消化空行
    		for(i=0;i<n;i++) gets(map[1][i]);
    		queue<node>q; 
    		temp.x=temp.y=temp.z=temp.t=0;q.push(temp); 
    		int find=0,nx,ny,nz,mt;
    		while(!q.empty()){
    			fr=q.front();
    			for(i=0;i<4;i++){
    				nx=fr.x+dir[i][0];ny=fr.y+dir[i][1];nz=fr.z;
    				if(nx<0||nx==n||ny<0||ny==m) continue;
    				switch(map[nz][nx][ny]){//对下一结点进行处理
    					case '#':switch(map[cs(nz)][nx][ny])
    						{case '*':case '#'://若传输到墙或传输机,将两处都处理为墙					
    							map[0][nx][ny]='*';map[1][nx][ny]='*';break;
    						case '.':map[nz][nx][ny]='*';
    						temp.x=nx;temp.y=ny;temp.z=cs(nz);temp.t=fr.t+1;q.push(temp);break;
    						case 'P':mt=fr.t+1;find=1;break;}
    					case '*':break;
    					case '.':temp.x=nx;temp.y=ny;temp.z=nz;temp.t=fr.t+1;q.push(temp);break;
    					case 'P':mt=fr.t+1;find=1;break;
    				}
    				if(find==1) break;
    			}
    			if(find==1) break;
    			map[fr.z][fr.x][fr.y]='*';//队首标记为墙,表示已访问过
    			q.pop();
    		}
    		if(find==1&&mt<=T) printf("YES\n");
    		else printf("NO\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    FZU-2150 Fire Game

    【题目】
    有一个n行m列的迷宫,每个网格要么是草地,要么是空地。选择2块草地(可以选同一块)点火并开始计时,火会向相邻的草地(东南西北)传播,速度为每秒一格。如果下一秒没有新的草地着火,则计时在当前时刻结束。如果计时结束后所有的草地都已着火,则视为有解,解的值为计时结束时刻。
    输入迷宫地图,求最小解。

    【思路】
    分别在2个格子同时点火
    (1)2路火可能会相遇
    2路火不一定同时到达同一个格子,除了相遇点。每个格子在着火之后即标记已访问。
    (2)是否烧完
    遍历所有#,看是否都被访问过。
    【注】不考虑起点相同,则答案错误。1个起点所花时间竟然比2个起点少,无法理解。

    #include
    #include
    #include
    const int N=15;
    const int inf=N*N;
    using namespace std;
    int n,m,r1,r2,c1,c2;//n为行数,m为列数
    char g[N][N];bool vis[N][N];
    struct node
    	{int r,c,d;}tnode;//r为行标,c为列标,d为最短路长度 
    const int dr[] = {1, 0, -1, 0};
    const int dc[] = {0, 1, 0, -1};
    bool inside(int r, int c) 
    {return r>=0&&r<n&&c>=0&&c<m;}
    int bfs(){//返回这2个点火点的最短时间
    	memset(vis,true,sizeof(vis));
    	vis[r1][c1]=false;vis[r2][c2]=false;
    	int ans=-1;
    	queue<node>q;
    	tnode.r=r1;tnode.c=c1;tnode.d=0;q.push(tnode);
    	tnode.r=r2;tnode.c=c2;tnode.d=0;q.push(tnode);
    	while(!q.empty()){
    		node u=q.front();q.pop();
    		if(u.d>ans) ans=u.d;
    		for(int i=0;i<4;i++){
    			int vr=u.r+dr[i],vc=u.c+dc[i];
    			if(inside(vr,vc)&&g[vr][vc]=='#'&&vis[vr][vc])
    				{tnode.r=vr;tnode.c=vc;tnode.d=u.d+1;q.push(tnode);
    				vis[vr][vc]=false;}
    		}
    	}
    	return ans;
    }
    bool check(){
    	for(int r=0;r<n;r++)
    		for(int c=0;c<m;c++)
    			if(g[r][c]=='#'&&vis[r][c])
    				return false;
    	return true;
    }
    int solve(){//返回每组最短等待时间 
    	int time=inf;
    	for(r1=0;r1<n;r1++)//每个点火点1 
    		for(c1=0;c1<m;c1++)
    			if(g[r1][c1]=='#'){//可以点火 
    				int first=1;
    				//第一个尝试的点火点2列标与点火点1相同,之后每行都是从头开始 
    				for(r2=r1;r2<n;r2++)//每个点火点2
    					for(c2=0;c2<m;c2++){
    						if(first) {first=0;if(r2==r1) c2=c1+1;}
    						if(g[r2][c2]=='#'){//可以点火
    							int temp=bfs();
    							if(check()&&temp<time) time=temp;
    						}
    					}
    			}
    	if(time<inf) return time;
    	return -1;
    }
    int main(){
    	int T,t,i;
    	scanf("%d",&T);
    	for(t=1;t<=T;t++){		
    		scanf("%d%d",&n,&m);getchar();
    		for(i=0;i<n;i++) gets(g[i]);
    		printf("Case %d: %d\n",t,solve());
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
  • 相关阅读:
    Linux笔记--vi编辑器
    动态规划--背包问题
    springboot整合阿里大于并结合mq发送短信
    MLC-LLM 部署RWKV World系列模型实战(3B模型Mac M2解码可达26tokens/s)
    构造函数_Date构造函数
    ES6内置对象 - Map
    前端使用firebase配置第三方登录介绍(谷歌登录,facebook登录等)
    Jenkins的Pipeline概念
    wuzhicms代码审计
    众贷贡献的DOT已经解锁,各类应用也已准备就绪
  • 原文地址:https://blog.csdn.net/holeer/article/details/134492547