zoukankan      html  css  js  c++  java
  • 缩点

    老师讲图论-缩点-复习

    我想 我没学过 缩点啊 (OTL)

    先讲 割点

    定义 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点

    P3388 【模板】割点(割顶)

    主要思想

    观察(DFS)搜索树,我们可以发现有两类节点可以成为割点:

    • 对根节点(u),若其有两棵或两棵以上的子树,则该根结点u为割点;

    • 对非叶子节点(u)(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。

    我们用(dfn[u])记录节点u在DFS过程中被遍历到的次序号,(low[u])记录节点(u)(u)的子树通过非父子边追溯到最早的祖先节点(即(DFS)次序号最小),那么(low[u])的计算过程如下:

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= (x)&&(x) <= '9')
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 20010,MAXM = 200010;
    bool vis[MAXN],out[MAXN];
    int head[MAXN],cnt,dfn[MAXN],low[MAXN],fa[MAXN],tot,root;
    struct node
    {
    	int v,next;
    }edge[MAXM];
    inline void addedge(int u,int v)
    {
    	edge[++cnt].v = v;
    	edge[cnt].next = head[u];
    	head[u] = cnt;
    }
    inline void Tarjan(int x)
    {
    	vis[x] = 1,dfn[x] = low[x] = ++tot;
    	int kid = 0;
    	for(reg i = head[x];i;i = edge[i].next)
    	{
    		int v = edge[i].v;
    		if(!vis[v])
    		{
    			Tarjan(v);
    			if(x == root)kid++;
    			fa[v] = x;
    			low[x] = min(low[x],low[v]);
    			if(x != root&&low[v] >= dfn[x]) out[x] = 1;
    		}
    		low[x] = min(low[x],dfn[v]); 
    	}
    	if(x == root&&kid >= 2)
    		out[x] = 1;
    }
    int main()
    {
    	int n = Read(1),m = Read(1);
        for(reg i = 1;i <= m;i++)
        {
        	int u = Read(1),v = Read(1);
        	addedge(u,v),addedge(v,u);
    	}
    	for(reg i = 1;i <= n;i++)
    		if(!vis[i])
    		{
    			root = i;
    			Tarjan(i);
    		}
    	int size = 0;
    	for(reg i = 1;i <= n;i++) if(out[i]) size++;
    	printf("%d
    ",size);
    	for(reg i = 1;i <= n;i++)
    		if(out[i]) printf("%d ",i);
    	return 0;
    }
    

    缩点

    就将强连通分量的点 缩成一个点

    P3387 【模板】缩点

    补充:(DAG) 有向无环图

    (Tarjan)缩点(+DAGdp)

    (DAGdp)(dp)

    如果是这样的(dp[son]=f(dp[fa]))
    先要拓扑排序

    #include <stack>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= x&&x <= '9') 
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 1e4 + 10,MAXM = 1e5 + 10;
    bool vis[MAXN],instack[MAXN];
    stack<int> st;
    vector<int> seq,ahead[MAXN],behind[MAXN];
    int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot;
    struct point
    {
    	int scc,dfn,low,power;
    }poi[MAXN];
    struct SCC
    {
    	int power,In;
    }scc[MAXN];
    struct node
    {
    	int u,v,next;
    }edge[MAXM];
    inline void addedge(int u,int v)
    {
    	edge[++cnt].v = v;
    	edge[cnt].u = u;
    	edge[cnt].next = head[u];
    	head[u] = cnt;
    }
    inline void tarjan(int x)
    {
    	poi[x].dfn = poi[x].low = ++tot;
    	vis[x] = 1,st.push(x),instack[x] = 1;
    	for(reg i = head[x];i;i = edge[i].next)
    	{
    		int v = edge[i].v;
    		if(!vis[v])
    		{
    			tarjan(v);
    			poi[x].low = min(poi[x].low,poi[v].low);
    		} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
    	}
    	if(poi[x].dfn == poi[x].low)
    	{
    		scc[++cnt_scc].power = scc[++cnt_scc].In = 0;
    		while(1)
    		{
    			int t = st.top();st.pop();
    			poi[t].scc = cnt_scc;instack[t] = 0;
    			scc[cnt_scc].power += poi[t].power;
    			if(t == x) break;
    		}
    	}
    	return;
    }
    inline void topo()
    {
    	queue<int> q;
    	for(reg i = 1;i <= cnt_scc;i++)
    		if(!scc[i].In) q.push(i);
    	while(!q.empty())
    	{
    		int it = q.front();q.pop();
    		seq.push_back(it);
    		for(reg i = 0;i < behind[it].size();i++)
    		{
    			int v = behind[it][i];
    			scc[v].In--;
    			if(!scc[v].In) q.push(v);
    		}
    	}
    }
    int main()
    {
    	n = Read(1);int m = Read(1);
    	for(reg i = 1;i <= n;i++) poi[i].power = Read(1);
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = Read(1),v = Read(1);
    		addedge(u,v);
    	}
    	for(reg i = 1;i <= n;i++)
    		if(!poi[i].dfn) tarjan(i);
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = edge[i].u,v = edge[i].v;
    		if(poi[u].scc != poi[v].scc)
    		{
    			int sccu = poi[u].scc,sccv = poi[v].scc;
    			scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu);
    		}	
    	}
    	topo();
    	for(reg i = 0;i < seq.size();i++)
    	{
    		int x = seq[i];
    		dp[x] = scc[x].power;
    		for(reg j = 0;j < ahead[x].size();j++)
    			dp[x] = max(dp[ahead[x][j]] + scc[x].power,dp[x]);
    		ans = max(ans,dp[x]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    P2002 消息扩散

    (Tarjan)缩点后 求入度为零的点の数

    #include <stack>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= x&&x <= '9') 
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 1e5 + 10,MAXM = 5e5 + 10;
    bool vis[MAXN],instack[MAXN];
    stack<int> st;
    vector<int> seq,ahead[MAXN],behind[MAXN];
    int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot;
    struct point
    {
    	int scc,dfn,low;
    }poi[MAXN];
    struct SCC
    {
    	int In;
    }scc[MAXN];
    struct node
    {
    	int u,v,next;
    }edge[MAXM];
    inline void addedge(int u,int v)
    {
    	edge[++cnt].v = v;
    	edge[cnt].u = u;
    	edge[cnt].next = head[u];
    	head[u] = cnt;
    }
    inline void tarjan(int x)
    {
    	poi[x].dfn = poi[x].low = ++tot;
    	vis[x] = 1,st.push(x),instack[x] = 1;
    	for(reg i = head[x];i;i = edge[i].next)
    	{
    		int v = edge[i].v;
    		if(!vis[v])
    		{
    			tarjan(v);
    			poi[x].low = min(poi[x].low,poi[v].low);
    		} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
    	}
    	if(poi[x].dfn == poi[x].low)
    	{
    		scc[++cnt_scc].In = 0;
    		while(1)
    		{
    			int t = st.top();st.pop();
    			poi[t].scc = cnt_scc;instack[t] = 0;
    			if(t == x) break;
    		}
    	}
    	return;
    }
    int main()
    {
    	n = Read(1);int m = Read(1);
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = Read(1),v = Read(1);
    		addedge(u,v);
    	}
    	for(reg i = 1;i <= n;i++)
    		if(!poi[i].dfn) tarjan(i);
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = edge[i].u,v = edge[i].v;
    		if(poi[u].scc != poi[v].scc)
    		{
    			int sccu = poi[u].scc,sccv = poi[v].scc;
    			scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu);
    		}	
    	}
    	for(reg i = 1;i <= cnt_scc;i++)
    		ans += !scc[i].In;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    P1262 间谍网络

    先判是否有解 (DFS)

    (Tarjan)缩点后,求入读为零的点的权值之和最小(缩点后的点的权值保持前面的点权最小值)

    #include <stack>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= x&&x <= '9') 
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 1e5 + 10,MAXM = 5e5 + 10;
    bool vis[MAXN],esp[MAXN],instack[MAXN];
    stack<int> st;
    int area,n,cnt,cnt_scc,ans,head[MAXN],tot;
    struct point {int scc,dfn,low,cost;} poi[MAXN];
    struct SCC {int In,cost;} scc[MAXN];
    struct node {int u,v,next;} edge[MAXM];
    inline void addedge(int u,int v)
    {
    	edge[++cnt].v = v;
    	edge[cnt].u = u;
    	edge[cnt].next = head[u];
    	head[u] = cnt;
    }
    inline void dfs(int x)
    {	
    	vis[x] = 1;
    	++area;
    	for(reg i = head[x];i;i = edge[i].next)
    	{
    		int v = edge[i].v;
    		if(!vis[v]) dfs(v);
    	}
    	return;
    }
    inline void tarjan(int x)
    {
    	poi[x].dfn = poi[x].low = ++tot;
    	vis[x] = 1,st.push(x),instack[x] = 1;
    	for(reg i = head[x];i;i = edge[i].next)
    	{
    		int v = edge[i].v;
    		if(!vis[v])
    		{
    			tarjan(v);
    			poi[x].low = min(poi[x].low,poi[v].low);
    		} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
    	}
    	if(poi[x].dfn == poi[x].low)
    	{
    		++cnt_scc;
    		while(1)
    		{
    			int t = st.top();st.pop();
    			scc[cnt_scc].cost = min(scc[cnt_scc].cost,poi[t].cost);
    			poi[t].scc = cnt_scc,instack[t] = 0;
    			if(t == x) break;
    		}
    	}
    	return;
    }
    int main()
    {
    	memset(scc,0,sizeof(scc));
    	n = Read(1);
    	int p = Read(1),m;
    	for(reg i = 1;i <= n;i++) scc[i].cost = poi[i].cost = MAXN;
    	for(reg i = 1;i <= p;i++)
    	{
    		int k;
    		poi[k = Read(1)].cost = Read(1);
    		esp[k] = 1;
    	}
    	m = Read(1);
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = Read(1),v = Read(1);
    		if(u == v) continue;
    		addedge(u,v);
    	}
    	for(reg i = 1;i <= n;i++) if(esp[i]) dfs(i);
    	if(area < n)
    	{
    		printf("NO
    ");
    		for(reg i = 1;i <= n;i++)
    			if(!vis[i])
    			{
    				printf("%d",i);
    				return 0;
    			}
    	}
    	memset(vis,0,sizeof(vis));
    	for(reg i = 1;i <= n;i++)
    		if(!poi[i].dfn) tarjan(i); 
    	for(reg i = 1;i <= m;i++)
    	{
    		int u = edge[i].u,v = edge[i].v;
    		if(poi[u].scc != poi[v].scc)
    		{
    			int sccu = poi[u].scc,sccv = poi[v].scc;
    			scc[sccv].In++;
    		}	
    	}
    	for(reg i = 1;i <= cnt_scc;i++)
    		if(!scc[i].In)
    			ans += scc[i].cost;
    	printf("YES
    %d
    ",ans);
    	return 0;
    }
    

    还有一些题

    P1407 [国家集训队]稳定婚姻

    P1455 搭配购买

    P2169 正则表达式

    P2194 HXY烧情侣

    P2515 [HAOI2010]软件安装

  • 相关阅读:
    java实现RSA非对称加密
    lombok中的@Builder注解
    java实现大文件的分割与合并
    IDEA新建springboot选择DevTools
    bat命令自动配置java环境变量
    java实现发送邮件
    随记
    编译原理学习——FIRST和LASTVT
    国王的游戏
    JAVA类加载及NEW对象的过程
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11780710.html
Copyright © 2011-2022 走看看