tarjan算法——割边

今天也是小小的学了一个tarjan算法中的割边的一个应用

他和割点很像,都是用来处理无向图的,只不过是不能走反向边罢了

我们首先来说一个割边的定义

割边

当我们在无向图中删除一个边,无向图被分成不联通的两部分,那么这条边就是割边

我们来看一下割边的模版代码的注释

1.变量名和数组

vector<pair<int,int>> edge;//边集
vector<int> e[200005];//每个点的出边集
int dfn[200005];//每个点的深搜序标记
int low[200005];//每个点的回溯值
int len;
vector<pair<int,int>> brige;//统计割边的数组

2.加边函数代码

void add(int v,int u)
{
	edge.push_back({v,u});
	e[v].push_back(edge.size()-1);//边的序号是从0开始,可以通过异或1查看是否是反边
}

3.tarjan函数代码

void tarjan(int v,int from)//from表示边来源的序号 
{
	dfn[v]=low[v]=++len;//给每个点打上标记
	for(int xu:e[v])//去寻找当前点出边的序号
	{
		int u=edge[xu].second;//寻找当前点的子节点
		if(dfn[u]==0)//如果没有访问过
		{
			tarjan(u,xu);
			low[v]=min(low[v],low[u]);
			if(low[u]>dfn[v])//是割边
			{
				brige.push_back({v,u});
			}
		}
		else if(xu!=(from^1))//查询过并且不是反边
		{
			low[v]=min(low[v],dfn[u]);
		}
	}
}

例题

T103481 【模板】割边

思路:割边的板题,直接写就OK了,用上面的板子即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int u,v;
vector<pair<int,int>> edge;//边集
vector<int> e[200005];//每个点的出边集
int dfn[200005];
int low[200005];
int len;
vector<pair<int,int>> brige;
int cnt; 
void add(int v,int u)
{
	edge.push_back({v,u});
	e[v].push_back(edge.size()-1);
}
void tarjan(int v,int from)//from表示边来源的序号 
{
	dfn[v]=low[v]=++len;
	for(int xu:e[v])
	{
		int u=edge[xu].second;
		if(dfn[u]==0)
		{
			tarjan(u,xu);
			low[v]=min(low[v],low[u]);
			if(low[u]>dfn[v])
			{
				brige.push_back({v,u});
			}
		}
		else if(xu!=(from^1))
		{
			low[v]=min(low[v],dfn[u]);
		}
	}
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;i++)
	{
		if(dfn[i]==0)
		tarjan(i,-1);
	}
	cout<<brige.size();
	return 0;
}

P1656 炸铁路

思路:也是割边的模版题,只不过需要对其进行排序即可,反正用的也是pair直接排序就好了,不需要重载

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int u,v;
vector<pair<int,int>> edge;
vector<int> e[200005];
int dfn[200005];
int low[200005];
int len;
vector<pair<int,int>> brige;
void add(int v,int u)
{
	edge.push_back({v,u});
	e[v].push_back(edge.size()-1);
}
void tarjan(int v,int from)
{
	dfn[v]=low[v]=++len;
	for(int xu:e[v])
	{
		int u=edge[xu].second;
		if(dfn[u]==0)
		{
			tarjan(u,xu);
			low[v]=min(low[v],low[u]);
			if(low[u]>dfn[v])
			{
				brige.push_back({v,u});
			}
		}
		else if(xu!=(from^1))
		{
			low[v]=min(low[v],dfn[u]);
		}
	}
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;i++)
	{
		if(dfn[i]==0)
		tarjan(i,-1);
	}
	sort(brige.begin(),brige.end());
	for(auto u:brige)
	{
		cout<<u.first<<" "<<u.second<<"\n";
	}
	return 0;
}

 P7687 [CEOI2005] Critical Network Lines

 思路:唯一不同的地方就是要去维护一个子树内的a的网络的数量或者b网络的数量,并且所有关键路径一定都是割边,我们只需要找到割边里面的满足子节点里面包括a的数量是0或者k,b的数量为0或者l的即可

#include<bits/stdc++.h>  
using namespace std;  

const int MAXN = 100005;  
struct Node {  
    int dfn, low, suma, sumb;  
};  

int n, m, k, l;  
int u, v;  
vector<pair<int, int>> edge;  
vector<int> e[MAXN];  
vector<pair<int, int>> bridge;  
Node nodes[MAXN];  
int len;  

void addEdge(int v, int u) {  
    edge.emplace_back(v, u);  
    e[v].push_back(edge.size() - 1);  
}  

void tarjan(int v, int from) {  
    nodes[v].dfn = nodes[v].low = ++len;  

    for (auto xu : e[v]) {  
        int u = edge[xu].second;  

        if (nodes[u].dfn == 0) {  
            tarjan(u, xu);  
            nodes[v].low = min(nodes[v].low, nodes[u].low);  
            nodes[v].suma += nodes[u].suma;  
            nodes[v].sumb += nodes[u].sumb;  

            if (nodes[u].low > nodes[v].dfn && ((nodes[u].suma == 0 || nodes[u].sumb == 0) || (nodes[u].suma == k || nodes[u].sumb == l))) {  
                bridge.emplace_back(v, u);  
            }  
        } else if (xu != (from ^ 1)) {  
            nodes[v].low = min(nodes[v].low, nodes[u].dfn);  
        }  
    }  
}  

signed main() {  
    ios::sync_with_stdio(false);  
    cin.tie(0);  
    
    cin >> n >> m >> k >> l;  
    for (int i = 1; i <= k; i++) {  
        cin >> u;  
        nodes[u].suma = 1; // 关键节点的计数  
    }  
    for (int i = 1; i <= l; i++) {  
        cin >> u;  
        nodes[u].sumb = 1; // 另外一类关键节点的计数  
    }  
    for (int i = 1; i <= m; i++) {  
        cin >> u >> v;  
        addEdge(u, v);  
        addEdge(v, u);  
    }  
    for (int i = 1; i <= n; i++) {  
        if (nodes[i].dfn == 0) {  
            tarjan(i, -1);  
        }  
    }  

    cout << bridge.size() << "\n";  
    for (const auto& br : bridge) {  
        cout << br.first << " " << br.second << "\n";  
    }  
    return 0;  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值