离线算法+并查集+逆向思维 P1197 [JSOI2008] 星球大战


何为离线化算法

在日常刷题的过程中,尤其是图论,贪心的部分题目给出的数据并不能保证求解的先后顺序,所以我们需要先将给出的数据保证在一个数组中,按照特定的顺序去处理这些数据,有点类似于图论中的 拓补排序


一、离线算法的优点

离线算法由于其对数据的处理步骤是经过特殊安排的,所以它在一定程度上运行时间是优于在线算法的。

原因如下:在线算法在处理当前数据时可能存在本应该按照时间或者其他某种序列在当前数据前处理的数据位置位于当前数据后,所以在处理完后面的数据时又必须返回当前数据更新当前数据的值。

而使用离线算法能够很好的避免这种重复运算

二、例题分析(离线+并查集)

P1197 JSOI2008 星球大战

1.分析题意:

秉承着正难则反的策列,我们使用逆向思维:题目给出的背景是破坏某个节点,在我们学习的很多算法中破坏节点实现的难度较大,so我们采取的应对方式时,将其看为重建。

重建的过程和添加新节点进入并查集的过程不能说毫不相干,只能说一模一样。

还有一点我们需要注意的是当一条边位于当前图中的前提条件是:这条边的两个顶点在当前时间内都位于图中。


2.实现过程

前驱处理:

首先我们先将各条边保存在数组中。以及记录个个节点重建的时间这段代码如下:

int times[400050] = {0};//保存节点重建的时间
struct X{
	int s,e,ma;
}x[200050];//保存边的起点,终点,以及添加入图中的时间
vector<int> edges[400050]; 
//记录各个时间新添加入图中的边在x数组中的下标

	for(int i = 1;i<=m;++i){
		cin >> x[i].s >>x[i].e;
	}
	cin >> k;
	for(int i = k;i;--i){//逆向处理最开始删除的节点就是最后重建的节点
		cin >> ff[i];
		times[ff[i]] = i;
	}
	for(int i = 1;i<=m;++i){
		x[i].ma = max(times[x[i].s],times[x[i].e]);
	//一条边新填入节点的时间就是当前边的两个顶点
	//中重建时间最后的一个顶点的重建时间
		edges[x[i].ma].push_back(i);
	}

更多细节:

1.在该题中存在下标为0的顶点,所以我们在初始化并查集时不能将数组中的值初始化为0,而是-1.(当然还有另外一种初始化是初始化为当前节点的下标,我比较习惯初始化为特定值).
2.输出答案时记得按照题目的要求

3.总体代码来啦:

#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
int times[400050] = {0};
struct X{
	int s,e,ma;
}x[200050];
int fa[400050] = {0};//并查集数组
int ff[400050] = {0};
vector<int> edges[400050]; 

void add(int a,int b){
	if(a==b) return;
	if(a>b){
		fa[a] = b;
	}else{
		fa[b] = a;
	}
}

int fs(int a){
	while(fa[a]>=0){
		a = fa[a];
	}
	return a;
}

int main(){
	ios::sync_with_stdio(0);//
	cin.tie(0);cout.tie(0);// cin/cout 的加速
	int n,m,k;
	cin >> n >>m;
	memset(fa,-1,sizeof(fa));
	for(int i = 1;i<=m;++i){
		cin >> x[i].s >>x[i].e;
	}
	cin >> k;
	for(int i = k;i;--i){
		cin >> ff[i];
		times[ff[i]] = i;
	}
	for(int i = 1;i<=m;++i){
		x[i].ma = max(times[x[i].s],times[x[i].e]);
		edges[x[i].ma].push_back(i);
	}
	int ans = n-k;
	for(int i = 0;i<=k;++i){
		if(i!=0) ans++;
		//因为当前节点刚重建属于孤立点所以此时连通块的数目应该++
		for(int j = 0;j<edges[i].size();++j){
			int aa = fs(x[edges[i][j]].s);
			int bb  = fs(x[edges[i][j]].e);
			add(aa,bb);
			if(fa[aa]!=-1 || fa[bb] !=-1){
				--ans;
			}
		}
		ff[i] = ans;
	}
	for(int i = k;i>=0;--i) cout << ff[i] <<endl;
}

总结:

离线算法的适用场景十分广泛,如果发现某个题目中未声明数据的大小关系或者时间顺序时,建议使用离线防止出现wa。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值