zoukankan      html  css  js  c++  java
  • 无向图边双联通分量+缩点

    无向图边双联通分量+缩点

    先求桥,再遍历一遍图,把桥去掉

    
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <deque>
    #include <map>
    #include <iostream>
    using namespace std;
    typedef long long LL;
    const double pi = acos(-1.0);
    const double e = exp(1);
    //const int MAXN =2e5+10;
    const LL N = 1000000007;
    
    struct edge
    {
    	int id;
    	int flag;
    	int from;
    	int to;
    	int next;
    } edge[200009], edge3[200009];
    int head[200009];
    int head3[200009]; //描述新图中结点之间的关系
    
    int dfn[100009];
    int low[100009];
    int cnt = 1;
    int cnt3 = 0; //用于构建缩点后新图的链式前向星
    
    int New[100009];	 //第i个双联通分量所含有的结点个数
    int captain[100009]; //点i所在的边双连通分量
    
    int father[100009];
    int vis[100009];
    int deep[100009];
    int sum;
    
    void tarjan(int u, int id)  //需要考虑去重边的问题
    {
    	int i;
    	low[u] = dfn[u] = cnt++;
    	for (i = head[u]; i != -1; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		if(id == edge[i].id)     //判断是不是同一条边,若id相同则为同一条
    			continue;
    		if (!dfn[v])
    		{
    			tarjan(v, edge[i].id);
    			low[u] = min(low[u], low[v]);
    			if (low[v] > dfn[u])
    			{
    				edge[i].flag = edge[i ^ 1].flag = 1; // 标记割边,在寻找边双连通分量时忽略掉该边
    
    			//	printf("%d ---> %d
    ", edge[i].from, edge[i].to);
    			}
    		}
    		else
    		{
    			low[u] = min(low[u], dfn[v]);
    		}
    	}
    }
    
    void seek_doubleEdge(int u, int cnt2)
    {
    	int i;
    	dfn[u] = cnt2;
    	New[cnt2]++;
    	captain[u] = cnt2;
    	for (i = head[u]; i != -1; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		if (edge[i].flag)
    		{
    
    			if (captain[v])
    			{
    				int a = captain[u];
    				int b = captain[v];
    
    				edge3[cnt3].to = b;
    				edge3[cnt3].next = head3[a];
    				head3[a] = cnt3++;
    
    				edge3[cnt3].to = a;
    				edge3[cnt3].next = head3[b];
    				head3[b] = cnt3++;
    
    				//	cout << u << " " << v << " " << a << " "<< b << " ** " << endl;
    			}
    			continue;
    		}
    		else
    		{
    			if (!dfn[v])
    			{
    				seek_doubleEdge(v, cnt2);
    			}
    		}
    	}
    }
    
    void init_lca(int u, int fa, int d)
    {
    	int i;
    	int num = 0;
    	deep[u] = d;
    	father[u] = fa;
    	//	printf("%d  %d  ?? 
    ", u, deep[u]);
    	for (i = head3[u]; i != -1; i = edge3[i].next)
    	{
    		int v = edge3[i].to;
    		if (!deep[v])
    		{
    			//printf("%d %d %d  (^_^)
    ", v, u, d);
    			num++;
    			init_lca(v, u, d + 1);
    		}
    	}
    
    	if(num == 0 || (u == 1 && num == 1))
    	{
    		//cout << u << endl;
    		sum++;
    	}
    }
    
    
    int main()
    {
    	int n, m, i;
    	int cnt1, a, b, q, nn = 0;
    	while (scanf("%d%d", &n, &m) != EOF)
    	{
    		if (n == 0 && m == 0)
    			break;
    		nn++;
    		cnt1 = 0;
    		cnt = 1;
    		memset(head, -1, sizeof(head));
    		memset(dfn, 0, sizeof(dfn));
    		memset(low, 0, sizeof(low));
    		memset(captain, 0, sizeof(captain));
    		while (m--)
    		{
    			scanf("%d%d", &a, &b);
    
    			edge[cnt1].id = cnt1;          //[1]
    			edge[cnt1].flag = 0;
    			edge[cnt1].from = a;
    			edge[cnt1].to = b;
    			edge[cnt1].next = head[a];
    			head[a] = cnt1++;
    
    			edge[cnt1].id = cnt1 - 1;      //[2],同[1]处注释一起为一条无向边的来回方向都标记同一个id.
    			edge[cnt1].flag = 0;
    			edge[cnt1].from = b;
    			edge[cnt1].to = a;
    			edge[cnt1].next = head[b];
    			head[b] = cnt1++;
    		}
    		for (i = 1; i <= n; i++) //双连通分量去割边
    		{
    			if (!dfn[i])
    			{
    				tarjan(i, -1);
    			}
    		}
    
    		memset(dfn, 0, sizeof(dfn));
    		memset(head3, -1, sizeof(head3));
    		memset(vis, 0, sizeof(vis));
    		memset(deep, 0, sizeof(deep));
    
    		int cnt2 = 0;
    		for (int i = 1; i <= n; i++) //标记双连通分量(缩点)
    		{
    			if (!dfn[i])
    			{
    				cnt2++;
    				seek_doubleEdge(i, cnt2);
    			}
    		}
    /*
    		cout << " ** " << cnt2 << endl; // 边双连通分量的个数
    		for (int i = 1; i <= n; i++)
    		{
    			printf("* %d   %d
    ", i, captain[i]);
    		}
    
    		
    		for (int i = 1; i <= cnt2; i++) //输出缩点后的新图
    		{
    			for (int j = head3[i]; j != -1; j = edge3[j].next)
    			{
    				cout << i << " " << edge3[j].to << endl;
    			}
    		}
    */
    		sum = 0;
    		init_lca(1, -1, 1);
    	//	cout << "sum: " << sum << endl;
    		if(sum == 1)
    			printf("0
    ");
    		else
    			printf("%d
    ", (sum + 1) / 2);
    
    /*		for (int i = 1; i <= cnt2; i++)
    		{
    			cout << i << " " << deep[i] << "  ?? " << endl;
    		}
    */
    		
    	}
    	return 0;
    }
    
  • 相关阅读:
    记一则玄乎奇玄的ADG误删自救事件
    ORACLE 日常信息查询sql
    Linux脚本判断磁盘容量
    postgresql数据库创建触发器记录表修改时间
    centos7关闭防火墙
    centos7 安装mysql5.7(二进制tar包方式)
    Oracle11G RMAN-06214: Datafile Copy /u01/app/oracle/product/11.2.0/db_1/dbs/snapcf_cpbd.f
    SQLPlus中set命令
    oradehub命令
    记一报错解决:ORA-00845: MEMORY_TARGET not supported on this system
  • 原文地址:https://www.cnblogs.com/daybreaking/p/12782877.html
Copyright © 2011-2022 走看看