FZU 2150 Fire Game 多起点BFS

这篇博客探讨了一个独特的火场游戏,玩家需要在限定的地图上找到两个起火点,使火势蔓延并烧尽所有草地。通过算法和逻辑分析,找出最短的时间来完成任务,涉及BFS广度优先搜索等知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem 2150 Fire Game

Accept: 655    Submit: 2512
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of this board is consisting of grass or just empty and then they start to fire all the grass. Firstly they choose two grids which are consisting of grass and set fire. As we all know, the fire can spread among the grass. If the grid (x, y) is firing at time t, the grid which is adjacent to this grid will fire at time t+1 which refers to the grid (x+1, y), (x-1, y), (x, y+1), (x, y-1). This process ends when no new grid get fire. If then all the grid which are consisting of grass is get fired, Fat brother and Maze will stand in the middle of the grid and playing a MORE special (hentai) game. (Maybe it’s the OOXX game which decrypted in the last problem, who knows.)

You can assume that the grass in the board would never burn out and the empty grid would never get fire.

Note that the two grids they choose can be the same.

Input

The first line of the date is an integer T, which is the number of the text cases.

Then T cases follow, each case contains two integers N and M indicate the size of the board. Then goes N line, each line with M character shows the board. “#” Indicates the grass. You can assume that there is at least one grid which is consisting of grass in the board.

1 <= T <=100, 1 <= n <=10, 1 <= m <=10

Output

For each case, output the case number first, if they can play the MORE special (hentai) game (fire all the grass), output the minimal time they need to wait after they set fire, otherwise just output -1. See the sample input and output for more details.

Sample Input

4
3 3
.#.
###
.#.
3 3
.#.
#.#
.#.
3 3
...
#.#
...
3 3
###
..#
#.#

Sample Output

Case 1: 1
Case 2: -1
Case 3: 0
Case 4: 2

题意:在地图上找两个合法的起点,每个点可以影响其上下左右四个相邻点(如果存在),问最少需要几步使所有的点都受到影响。

分析:

1.首先要做的当然就是找到那两个起点,直接枚举就行。

2.找到的两个起点可能是同一个点,也可能是不同的点,需要判断一下,相同就只让一个入队列,不同就两个都入队列。

3.之后就是单纯的BFS,入队出队,就是结束条件不太一样,这里的结束条件是当所有的草坪都被影响到,所以声明一个判断函数,然后就是每一次搜索的结果可能不会一样,有的搜索甚至找不到最终结果,所以,对于最终的结果,需要仔细处理

(1)只有当从未找到结果的时候,step才能为-1

(2)找到结果后,不仅要判断是不是步数更少,还要判断step是不是-1

#include <iostream>
#include <cmath>
#include <stdio.h>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <algorithm>
#include <memory.h>
#define MAX 20
using namespace std;
struct P//记录位置的结构体
{
    int r,c;
    int step;
};

struct Grass//记录所有草坪位置的结构体
{
    P p[MAX*MAX];
    int num;//草坪的数量
};
Grass grass;
char Map[MAX][MAX];//地图
int visit[MAX][MAX];//BFS中最重要的visit数组
int n,m;//行,列
int step;//最终所花的时间
int dirr[4]={-1,1,0,0};//方向数组
int dirc[4]={0,0,-1,1};



int judge()//判断是不是所有草坪都被烧光了
{
    int i;
    for(i=0;i<grass.num;i++)
    {
        if(visit[grass.p[i].r][grass.p[i].c]==0)
            return 0;
    }
    return 1;
}

void show()//调试时使用,输出visit数组
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cout<<visit[i][j]<<' ';
        }
        cout<<endl;
    }

}


void bfs(P p1,P p2)//广搜
{
    int i,j,R,C;
    int Max_step=0;//每一次搜索不同的起点所需的时间
    queue<P> q;
    if(p1.c==p2.c&&p1.r==p2.r)//两个起点相同
    {
        p1.step=0;
        q.push(p1);
        visit[p1.r][p1.c]=1;
    }
    else//两个起点不同
    {
        p1.step=0;
        p2.step=0;
        q.push(p1);
        q.push(p2);
        visit[p1.r][p1.c]=1;
        visit[p2.r][p2.c]=1;
    }
    while(!q.empty())
    {
        P temp=q.front();
        q.pop();
        if(judge())//判断是否全部烧完
        {
            if((Max_step<step)||(step==-1))//如果上次搜索所用的时间较多或者之前未找到结果
            {
                step=Max_step;
            }
            return;
        }
        for(i=0;i<4;i++)//四向搜索
        {
            R=temp.r+dirr[i];
            C=temp.c+dirc[i];
            if(R>=0&&R<n&&C>=0&&C<m&&Map[R][C]=='#'&&visit[R][C]==0)
            {
                P next;
                next.r=R;
                next.c=C;
                next.step=temp.step+1;
                if(Max_step<next.step)//记录本次搜索所需的最大时间
                    Max_step=next.step;
                q.push(next);
                visit[R][C]=1;
            }
        }
    }
    if(step==-1||step==999999)//如果从来都没有搜索到结果
    step=-1;
}

int main()
{
    int i,j,t,cas;
    cin>>t;
    for(cas=1;cas<=t;cas++)
    {
        step=999999;
        grass.num=0;
        cin>>n>>m;
        for(i=0;i<n;i++)//输入数据
        {
            for(j=0;j<m;j++)
            {
                cin>>Map[i][j];
                if(Map[i][j]=='#')
                {
                    grass.p[grass.num].r=i;
                    grass.p[grass.num].c=j;
                    grass.num++;
                }
            }
        }
        for(i=0;i<grass.num;i++)//枚举所有可能的起点,进行搜索
        {
            for(j=i;j<grass.num;j++)
            {
                memset(visit,0,sizeof(visit));//每次搜索都要初始化visit数组
                bfs(grass.p[i],grass.p[j]);
            }
        }
        cout<<"Case "<<cas<<": "<<step<<endl;
    }
    return 0;
}




<think>首先,用户的问题是关于“多起点多终点最短路径问题”的算法或实现方法。用户引用了之前的参考,但指出那只是参考,我需要基于用户的实际需求来回答。 用户的需求是:寻找解决多起点多终点最短路径问题的算法或实现方法。具体来说,是“多起点多终点最短路径算法解决方案”。 关键点: - 多起点:有多个起点。 - 多终点:有多个终点。 - 最短路径:需要找到从一个起点到一个终点的最短路径,或者更具体地说,可能是找到所有起点到所有终点的最短路径,或者最小化起点到终点的距离。 在参考中,提到了一个解决方案:使用SPFA算法,通过创建虚拟源点和汇点来处理多起点多终点问题。具体步骤是: - 创建一个虚拟源点(0),将其连接到所有起点,边权为0。 - 创建一个虚拟汇点(n+1),将所有终点连接到它,边权为0。 - 然后运行SPFA算法从源点0到汇点n+1,以找到最短路。 这似乎是处理多起点多终点问题的一种有效方法,因为它将问题转化为单源单汇问题。 现在,我需要根据系统级指令来构建回答: 1. **行内数学表达式**:使用$...$格式。例如,变量、距离等。 2. **独立公式**:使用$$...$$格式并单独成段。 3. **LaTeX语法正确**:确保所有数学表达式的语法正确。 4. **使用中文回答**:整个回答用中文。 5. **生成相关问题**:在回答末尾,生成3个相关问题。 6. **引用段落末尾添加引用标识**:如[^1]。在参考中,有引用[^1],所以我需要自然地引用它。 回答结构: - 由于用户要求结构清晰,帮助逐步解决问题,我应该: 1. 解释问题:什么是多起点多终点最短路径问题。 2. 描述解决方案:介绍SPFA算法结合虚拟节点的实现方法。 3. 提供伪代码或代码示例:类似于示例中的快速排序代码,我需要提供一个实现。 4. 讨论优化和注意事项:简要讨论性能、优化等。 5. 生成相关问题:在最后,列出3个相关问题。 在参考中,引用了[^1],是关于“I - 浪里个浪 FZU - 2261”的,描述中使用SPFA算法处理多起点多终点问题。所以,我可以引用这个作为依据。 步骤: 1. **介绍问题**:多起点多终点最短路径问题涉及在图中从任意起点到任意终点的最短路径。常见于交通网络、游戏路径查找等。 2. **解决方案**:使用虚拟源点和汇点来转换问题。 - 创建虚拟源点S,连接到所有起点,边权为0。 - 创建虚拟汇点T,所有终点连接到它,边权为0。 - 运行单源最短路径算法(如SPFA、Dijkstra)从S到T。 - 这样,从S到T的最短路径对应了从某个起点到某个终点的最短路径。 3. **算法实现**:提供SPFA算法的代码示例。 4. **优化**:讨论为什么使用SPFA(适用于可能有负权边的图),如果无负权边,Dijkstra更高效。 5. **引用**:在描述解决方案时,自然地添加引用标识。 伪代码结构: - 类似于示例,用代码块展示。 - 使用Python或伪代码。 完整回答框架: - 开头:解释问题。 - 主体:描述方法,包括数学表达式(如距离公式)。 - 代码:提供实现。 - 结尾:总结,并生成相关问题。 数学表达式: - 例如,距离$d(u,v)$,或图中的边权重。 - 在行内使用$...$。 相关问题:生成3个,类似于示例: 1. 如何优化多起点多终点最短路径算法? 2. 如果图中有负权边,如何处理? 3. 这个算法的时间复杂度是多少? 确保引用:在描述解决方案时,提到参考[^1]。 最终回答结构: - 问题解释。 - 解决方案描述。 - 代码实现。 - 相关问题。</think>### 多起点多终点最短路径问题解决方案 在多起点多终点最短路径问题中,给定一个图 $G=(V,E)$,其中 $V$ 是节点集合,$E$ 是边集合,每条边有权重 $w(e)$。目标是找到从任意起点 $s \in S$(起点集合)到任意终点 $t \in T$(终点集合)的最短路径,即最小化路径总权重 $\min_{s \in S, t \in T} d(s,t)$,其中 $d(s,t)$ 表示从 $s$ 到 $t$ 的最短距离[^1]。这种问题常见于交通网络优化、游戏路径规划或分布式系统路由等场景。 #### 核心算法:基于SPFA的虚拟节点方法 为解决此问题,一种高效方法是通过添加虚拟源点和汇点,将多起点多终点问题转化为单源单汇最短路径问题。具体步骤如下(基于SPFA算法,适用于一般图,包括可能有负权边的情况): 1. **问题转换**: - 创建虚拟源点 $0$,将其与所有起点 $s \in S$ 连接,添加边权为 $0$ 的无向边(即 $0 \to s$ 和 $s \to 0$)。 - 创建虚拟汇点 $n+1$(假设原图节点编号为 $1$ 到 $n$),将所有终点 $t \in T$ 连接到它,添加边权为 $0$ 的无向边(即 $t \to n+1$ 和 $n+1 \to t$)。 - 这样,从虚拟源点 $0$ 到虚拟汇点 $n+1$ 的最短路径,对应了原图中从某个起点到某个终点的最短路径。路径权重即为 $\min d(s,t)$。 2. **算法原理**: - 使用SPFA(Shortest Path Faster Algorithm)算法,它是一种基于队列优化的Bellman-Ford变体,平均时间复杂度为 $O(|V||E|)$,最坏情况 $O(|V||E|)$,但实践中效率较高,尤其适合稀疏图。 - 关键公式:设 $dis[u]$ 表示从源点 $0$ 到节点 $u$ 的最短距离,松弛操作为: $$ dis[v] = \min(dis[v], dis[u] + w(u,v)) \quad \forall (u,v) \in E $$ - 最终,$dis[n+1]$ 即为所求的最小距离。 3. **优化说明**: - 此方法避免了为每对起点-终点单独计算最短路径(否则复杂度高达 $O(|S||T||V|^2)$),而是通过一次SPFA调用解决。 - 如果图中无负权边,可改用Dijkstra算法(时间复杂度 $O(|E| + |V| \log |V|)$),更高效但需保证非负权重。 - 空间复杂度为 $O(|V| + |E|)$,适用于大规模图。 #### Python代码实现 以下代码使用SPFA算法实现上述方法。假设图以邻接表形式存储(`graph[u]` 包含元组 `(v, w)` 表示边 $u \to v$ 的权重 $w$)。 ```python from collections import deque def spfa_multiple_sources_targets(graph, sources, targets): n = len(graph) - 2 # 原图节点数,假设节点编号从1到n # 添加虚拟源点0和汇点n+1 graph[0] = [(s, 0) for s in sources] # 虚拟源点0连接到所有起点 graph[n+1] = [] # 虚拟汇点n+1 for t in targets: graph[t].append((n+1, 0)) # 所有终点连接到虚拟汇点 # 初始化距离数组和队列 dis = [float('inf')] * (n+2) dis[0] = 0 in_queue = [False] * (n+2) queue = deque([0]) in_queue[0] = True # SPFA主循环 while queue: u = queue.popleft() in_queue[u] = False for v, w in graph[u]: if dis[u] + w < dis[v]: dis[v] = dis[u] + w if not in_queue[v]: queue.append(v) in_queue[v] = True # 返回虚拟汇点的距离,即最小起点-终点距离 return dis[n+1] # 示例使用 if __name__ == "__main__": # 假设图有4个节点(1,2,3,4),起点S={1,2},终点T={3,4} graph = [[] for _ in range(6)] # 索引0到5(节点0和5为虚拟点) # 添加实际边:例如,边1->3权重2,边2->4权重3等 graph[1].append((3, 2)) graph[2].append((4, 3)) # 添加其他边... sources = [1, 2] targets = [3, 4] min_distance = spfa_multiple_sources_targets(graph, sources, targets) print(f"最小起点-终点距离: {min_distance}") ``` #### 注意事项 - **负权边处理**:SPFA能处理负权边,但如果存在负权环,算法可能不收敛(需添加环检测)。 - **性能分析**:在稀疏图中,SPFA平均效率较高;稠密图可考虑优化如SLF(Small Label First)策略。若无负权边,优先使用Dijkstra(代码类似,但用优先队列)。 - **实际应用**:此方法已用于竞赛编程问题(如FZU-2261),通过虚拟节点简化问题[^1]。 - **可靠性说明**:本方案基于图论标准实践,虚拟节点法在多个文献中被验证为有效优化[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值