【HDU - 5876】Sparse Graph(补图bfs,STLset)

探讨了在图论中,如何求解给定无向图的补图上从特定起点到所有其他点的最短路径问题。通过改变常规BFS算法策略,采用枚举点而非边的方法,有效减少了时间复杂度。

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

题干:

In graph theory, the complementcomplement of a graph GG is a graph HH on the same vertices such that two distinct vertices of HH are adjacent if and only if they are notnotadjacent in GG. 

Now you are given an undirected graph GG of NN nodes and MM bidirectional edges of unitunit length. Consider the complement of GG, i.e., HH. For a given vertex SS on HH, you are required to compute the shortest distances from SS to all N−1N−1 other vertices.

Input

There are multiple test cases. The first line of input is an integer T(1≤T<35)T(1≤T<35)denoting the number of test cases. For each test case, the first line contains two integers N(2≤N≤200000)N(2≤N≤200000) and M(0≤M≤20000)M(0≤M≤20000). The following MM lines each contains two distinct integers u,v(1≤u,v≤N)u,v(1≤u,v≤N) denoting an edge. And S (1≤S≤N)S (1≤S≤N)is given on the last line.

Output

For each of TT test cases, print a single line consisting of N−1N−1 space separated integers, denoting shortest distances of the remaining N−1N−1 vertices from SS (if a vertex cannot be reached from S, output ``-1" (without quotes) instead) in ascending order of vertex number.

Sample Input

1
2 0
1

Sample Output

1

题目大意:

给一个N个点M条边的无向图,给你一个圆点,让你在对应图的补图上求出起点S到每个点的最短路。

解题报告:

正常bfs是对于当前点来说枚举当前点的每一条边,看该点是否遍历过来决定是否进行入队操作。而对于这个题,因为补图的边的数量非常大,所以我们可以从点的角度入手,因为一共需要更新1e5个点,所以我们可以直接在每一次循环中都先枚举每一个点,看他和当前点是否有连边,如果原图中没有这条边的话那就可以直接更新这个点,并且把他放到队列中。通过枚举边变成枚举点,减少时间复杂度。也就是正常bfs是枚举每一个边看是否出点被更新过,这个题因为边数很多但是点数不多,所以可以枚举每一个没出现过的点,看这条边是否存在。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int MAX = 2e5 + 5;
set<int> ss;
set<int> vv[MAX];
int st,n,m;
int dis[MAX];
void bfs(int st) {
	for(int i = 1; i<=n; i++) dis[i] = INF;
	dis[st] = 0;
	ss.erase(st);
	queue<int> q;q.push(st);
	while(q.size()) {
		vector<int> tmp;
		int cur = q.front();q.pop();
		for(auto it = ss.begin();it!=ss.end(); ++it) {
			int v = *it;
			if(vv[cur].find(v) == vv[cur].end()) {
				dis[v] = dis[cur]+1;tmp.pb(v); q.push(v);
			} 
		}
		for(auto v : tmp) ss.erase(v);
	}
}
int main()
{
	int T;
	cin>>T;
	while(T--) {
		scanf("%d%d",&n,&m);
		ss.clear();
		for(int i = 1; i<=n; i++) ss.insert(i);
		for(int i = 1; i<=n; i++) vv[i].clear();
		for(int u,v,i = 1; i<=m; i++) scanf("%d%d",&u,&v),vv[u].insert(v),vv[v].insert(u);
		scanf("%d",&st);
		bfs(st);
		int cnt = 1;
		for(int i = 1; i<=n; i++) {
			if(i == st) continue;
			if(dis[i] == INF) printf("%d%c",-1,cnt == n-1 ? '\n' :' ') ;
			else printf("%d%c",dis[i],cnt == n-1 ? '\n' :' ');
			cnt++;
		}
	}
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值