【并查集】P1197 [JSOI2008]星球大战-洛谷

本文详细解析了如何运用逆向思维来解决洛谷P1197题目,通过并查集优化算法,避免重复扫描图并在修复操作中判断联通性。代码实现中,首先构建完整的图,然后模拟摧毁过程,逐步恢复节点并更新连通块数量。此方法提高了效率,降低了复杂度。

题目链接

https://www.luogu.com.cn/problem/P1197

在这里插入图片描述
在这里插入图片描述

解题思路

1.这题最容易想到的方法就是用并查集搜索;在每次摧毁一个节点之后,把与该节点相连的边全部擦去,重新扫描图;
	但是这个方法太费时,肯定不行;
2.`逆向思维`
  我们不妨在一开始建图的时候就不考虑会被摧毁的点,直接建最后的图;
  然后把每一次的摧毁操作当成修复操作;再利用并查集判断联通即可;

代码展示

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+10;
/*
1.存储所有边
2.存储所有被摧毁的点
3.遍历所有点,找出存在的边
4.依次恢复点,每次增加这个点能构成的所有边 
*/
struct edge{
	int from,to;
};
//存储边 
vector<int>G[maxn];
//存储图 
edge Edge[maxn];
bool ds[maxn];
int K[maxn];
int father[maxn];
int ans[maxn];
int Find(int x){
	if(x==father[x])
		return x;
	father[x]=Find(father[x]);
	return father[x];
}
int main(){
	int n,m;
	cin>>n>>m;
	int f=-1;
	for(int i=0;i<n;i++)father[i]=i;
	for(int i=0;i<m;i++){
		int a,b;
		cin>>a>>b;
		G[a].push_back(b);
		G[b].push_back(a);
		f++;
		Edge[f].from=a,Edge[f].to=b;
		f++;
		Edge[f].from=b,Edge[f].to=a;	
	}
	//初始化
	int k;
	cin>>k;
	for(int i=0;i<k;i++){
		cin>>K[i];
		ds[K[i]]=true;//被摧毁 
	} 
	int total=n-k;//值得注意的是,一开始的节点个数应该按照n-k处理 
	for(int i=0;i<f;i++){
		if(ds[Edge[i].from]==false&&ds[Edge[i].to]==false){
			//两个点都没被炸毁
			int ff=Find(Edge[i].from);
			int ft=Find(Edge[i].to);
			if(ff!=ft){
				father[ff]=ft;
				total--;
			} 
			
		}
	}
	ans[k]=total;
	for(int i=k-1;i>=0;i--){
		ds[from]=false;
		total++;//修复一个点,把这个点先当成独立的连通块 
		int from=K[i];
		for(int j=0;j<G[from].size();j++){
			int to=G[from][j];
			if(ds[to]==false){
			//一条边的两个点都没被炸毁
			int ff=Find(from);
			int ft=Find(to);
			if(ff!=ft){
				father[ff]=ft;
				total--;
			} 
			
	    	}
		} 
		ans[i]=total;
	}
	for(int i=0;i<=k;i++)
		cout<<ans[i]<<endl;
	return 0;
}

总结

1.这题的难度并不大,只要我们能转过来想一想;
2.另外就是在创建标记符的时候,像flag,一定要记住true与false分别代表什么意思;

拓展练习

https://www.luogu.com.cn/problem/P1653
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高冷小伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值