有向图缩点

/*****************************\
| 有向图缩点
| CALL:void reduce_point();
| N,M分别为点数边数
| fa[]记录缩点后每个点属于哪个点
\*****************************/
int f_min(int x,int y){
	return x<y?x:y;
}
struct Edge{
	int u,v,next,w;
};
Edge edge[100000];
int head[50000],en;
void insert(int u,int v,int w){
	edge[en].u=u;edge[en].v=v;edge[en].w=w;
	edge[en].next=head[u];head[u]=en++;
}
int N,M;
void get_data(){
	memset(head,-1,sizeof(head));
	en=0;
	int u,v,w;
	while(M--){
		scanf("%d%d%d",&u,&v,&w);
		insert(u,v,w);
	}
}
int fa[50000],stk[50000],sn,time,mint[50000];
bool instk[50000],mark[50000];
void dfs(int u){
	int now;
	now=mint[u]=time++;
	stk[sn++]=u;
	instk[u]=1;
	int i,v;
	for(i=head[u];i!=-1;i=edge[i].next){
		v=edge[i].v;
		if(mark[v])continue;
		if(!instk[v])dfs(v);
		mint[u]=f_min(mint[u],mint[v]);
	}
	if(mint[u]==now){
		while(stk[sn-1]!=u){
			fa[stk[sn-1]]=u;//stk[sn-1]属于u点,如有需要可在此保存
			mark[stk[sn-1]]=1;
			sn--;
		}
		fa[u]=u;mark[u]=1;sn--;
	}
}
void reduce_point(){
	memset(mark,0,sizeof(mark));
	memset(instk,0,sizeof(instk));
	sn=time=0;
	int i;
	for(i=0;i<N;i++){
		if(mark[i])continue;
		dfs(i);
	}
}

### 关于有向图最小生成树 对于无向图,最小生成树是一个经典的优化问题,可以通过 Kruskal 或 Prim 算法来解决。然而,在有向图的情况下,定义和求解最小生成树会有所不同。 #### 定义 在有向图中,“最小生成树”的概念通常被扩展为 **最小生成森林** 或者更具体地说是 **最小强连通分支树 (Arborescence)**。一个 Arborescence 是一种特殊的有向生成树,其中所有的都指向根节或者从根节出发[^3]。换句话说: - 如果存在一个指定的根节 \( r \),那么该有向图的一个最小生成树可以看作是从这个根节出发的一棵最小权值的树形结构。 这种情况下,我们称之为 **最小成本有向生成树 (Minimum Cost Directed Spanning Tree)** 或简称 **最小 Arborescence**。 #### Edmonds' Algorithm(Edmonds 算法) 针对有向图中的最小生成树问题,最著名的算法是由 Jack Edmonds 提出的一种贪心策略,称为 Edmonds’ algorithm。此算法能够有效地计算以某个特定顶作为根节的最小 Arborescence。以下是其核心思想: 1. 初始化:假设给定一个带权重的有向图 \( G=(V,E) \),以及选定的根节 \( r \in V \)[^4]。 2. 构建候选集合:对于每一个非根节 \( v \neq r \),选取进入 \( v \) 的具有最小权重的一条弧加入到当前候选集中。 3. 处理循环:如果这些选出的弧形成了若干个独立回路,则通过收技术把这些回路压成单个超级节,并重复上述过程直到不再形成新的回路为止。 4. 展开操作:最后一步是对之前所做的所有收动作逆序展开恢复原始图形并得到最终的结果。 这种方法的时间复杂度大约为 O(|E||V|),虽然不是最优效率级别,但在实际应用当中表现良好[^5]。 ```csharp // 下面给出的是伪代码框架用于说明逻辑流程而非完整的C#实现版本 public class Edge { public int From { get; set; } public int To { get; set; } public double Weight { get; set; } } List<Edge> FindMinSpanningArborescence(List<List<Edge>> graph, int root){ List<int>[] inverseGraph = BuildInverseGraph(graph); bool[] visitedNodes = new bool[graph.Count]; Array.Fill(visitedNodes, false); PriorityQueue<(int node, double cost),double> pq=new(); foreach(var edge in graph[root]){ pq.Enqueue((edge.To, edge.Weight), edge.Weight); } while(pq.TryDequeue(out var item)){ if(!visitedNodes[item.node]){ // Process the current minimum weight incoming arc... visitedNodes[item.node]=true; foreach(var outgoingEdge from graph[item.node]){ if(!visitedNodes[outgoingEdge.To]) UpdatePriorityQueueWithNewCosts(pq,outgoingEdge); } } } } ``` 上面展示了一个简化版的思路转换为程序设计形式的例子,注意这并非严格意义上的Edmond's Algorithm完全体,仅提供理解上的帮助[^6]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值