【洛谷P5068】我回来了【bfs】

本文详细解析洛谷P5068题目的算法解决方案,通过使用C++的bitset实现节点集合的高效操作,进行bfs遍历以计算节点间距离,并针对每次查询快速求解满足条件的节点数量。

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

题目

题目链接:https://www.luogu.com.cn/problem/P5068
珂朵莉给你一个无向图,每次查询的时候给一堆二元组 ( x i , y i ) (x_i,y_i) (xi,yi)
求图中有多少个点 u u u与至少一个这次询问给出的二元组 ( x i , y i ) (x_i,y_i) (xi,yi)满足 d i s t ( u , x i ) ≤ y i dist(u,x_i)\leq yi dist(u,xi)yi d i s t dist dist表示这两个点在图中的距离
如果不连通 d i s t = i n f dist = inf dist=inf


思路:

第一次写出来 Y n o i Ynoi Ynoi的题。虽然这是Ynoi为数不多的良心题(雾
p [ i ] [ j ] p[i][j] p[i][j]表示距离点 i i i最短路长度不超过 j j j的节点集合,那么我们只要求出长度等于 j j j的节点集合,然后或上 p [ i ] [ j − 1 ] p[i][j-1] p[i][j1]即可。
对于每一个点进行一次 b f s bfs bfs即可求出 p p p
然后对于每一次询问,其实答案节点集合就是 p [ x 1 ] [ y 1 ]   o r   p [ x 2 ] [ y 2 ]   o r   . . .   o r   p [ x t ] [ y t ] p[x_1][y_1]\ or\ p[x_2][y_2]\ or\ ...\ or\ p[x_t][y_t] p[x1][y1] or p[x2][y2] or ... or p[xt][yt]
p p p的类型是 c + + c++ c++自带的 b i t s e t bitset bitset。每一位只能是0或1,占用空间 1 b 1b 1b
时间复杂度玄学。
还有洛谷讨论区中说这道题似乎卡邻接表。。。用 v e c t o r vector vector存边即可。


代码:

#include <queue>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=1010,M=100010,Inf=1e9;
int n,m,Q,x,y,t,tot,dis[N];
bitset<N> p[N][N];
vector<int> e[N];

inline int read()
{
	int d=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9')
		d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

inline void bfs(int S)
{
	memset(dis,0x3f3f3f3f,sizeof(dis));
	queue<int> q;
	q.push(S); dis[S]=0; p[S][0][S]=1;
	while (q.size())
	{
		int u=q.front();
		q.pop();
		for (register int i=0;i<e[u].size();i++)
		{
			int v=e[u][i];
			if (dis[v]>Inf)
			{
				dis[v]=dis[u]+1;
				p[S][dis[v]][v]=1;
				q.push(v);
			}
		}
	}
	for (register int i=1;i<N;i++)
		p[S][i]|=p[S][i-1];
}

signed main()
{
	n=read(); m=read(); Q=read();
	for (register int i=1;i<=m;i++)
	{
		x=read(); y=read();
		e[x].push_back(y);
		e[y].push_back(x);
	}
	for (register int i=1;i<=n;i++)
		bfs(i);
	while (Q--)
	{
		t=read();
		bitset<N> ans(0);
		while (t--)
		{
			x=read(); y=read();
			ans|=p[x][y];
		}
		printf("%d\n",ans.count());
	}
	return 0;
}
### 解决方案 对于洛谷 P1331 海战问题,采用广度优先搜索(BFS)是一种有效的方法来遍历整个地图并统计符合条件的矩形区域数量。下面提供了一种基于Java语言的具体实现方法。 #### 初始化与输入读取 程序开始时需定义必要的变量用于存储地图数据以及访问标志位数组。通过标准输入获取地图尺寸N和M,并逐行读入字符矩阵表示的地图信息[^2]。 ```java import java.util.*; public class Main { static char[][] map; static boolean[][] visited; public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); // 行数 int M = sc.nextInt(); // 列数 map = new char[N][]; visited = new boolean[N][M]; for (int i = 0; i < N; ++i){ String line = sc.next(); map[i] = line.toCharArray(); } solve(N, M); } private static void solve(int n, int m) { int count = 0; Queue<int[]> queue = new LinkedList<>(); for (int row = 0; row < n; ++row) { for (int col = 0; col < m; ++col) { if (!visited[row][col]) { if ('.' == map[row][col]){ continue; } else{ if(isRectangle(queue, row, col)){ count++; } } } } } System.out.println(count); } } ``` #### 广度优先搜索函数设计 为了判断某个连通块是否构成合法矩形,在遇到未访问过的'X'位置启动一次BFS过程。此过程中不仅记录下该连通区域内所有坐标点,还同步验证其形状特性——即边界处应全部由'X'组成而内部为空白或已处理过的位置[^4]。 ```java private static final int[] dx = {-1, 1, 0, 0}; private static final int[] dy = {0, 0, -1, 1}; private static boolean isRectangle(Queue<int[]> q, int startRow, int startCol){ q.offer(new int[]{startRow, startCol}); visited[startRow][startCol] = true; List<int[]> points = new ArrayList<>(); while(!q.isEmpty()){ int[] curPos = q.poll(); points.add(curPos); for (int dir = 0; dir < 4; ++dir){ int newRow = curPos[0]+dx[dir], newCol=curPos[1]+dy[dir]; if(inBounds(map.length,map[0].length,newRow,newCol)&&!visited[newRow][newCol]&&map[newRow][newCol]=='X'){ visited[newRow][newCol]=true; q.offer(new int[]{newRow, newCol}); } } } return checkShape(points.toArray((new int[points.size()][]))); } // 辅助函数:检查给定点集组成的图形是否满足矩形条件 static boolean checkShape(int[][] shapePoints){...} // 需要自行补充逻辑 ``` #### 形状校验辅助功能编写 最后一步是在`checkShape()`中完成对收集到的所有节点坐标的几何属性检验工作,确保它们确实组成了一个完整的矩形结构而不是其他不规则形态。这部分具体实现在这里省略了,读者可以根据题目描述中的要求独立思考如何构建相应的判定准则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值