有向无环图最长路

  是昨天校内测的一题,听说也是cf上的一道原题,题意大致是说,有一个N*M的矩阵每个位置都有有一个正整数,需要你在保持每个数在其所在行列大小关系不变的情况下把图的的数缩小,并且每个数仍旧是正整数,问最小的图是长啥样的。

  数据范围 0≤Aij≤10^9 0≤N*M≤10^6   

  很明显这是一个有向无环图的最长路问题,讲道理就是拓扑排序,然而那时候有点犯浑,写好之后出了小问题,愣是写了spfa刷最长路,10^6强行跑,还觉得能过得。。最后TLE三个点直播尴尬,这道题还存在一个问题就是相同点的连边,注意到了怎么处理都可以,建议并查集,如果是刷spfa之类的也可以把相同点连双向边,不过要注意边数的大小。

 贴代码↓

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> node;
const int maxv=1000010;
int map[maxv],fa[maxv],lk[maxv],nextt[maxv<<1],son[maxv<<1],tot,D[maxv],V[maxv],n,m,que[maxv],head,tail;
node temp[maxv];
int g(const int &x,const int &y) {
	return (x-1)*m+y;
}
int getfather(int x){
	return fa[x]==x?x:fa[x]=getfather(fa[x]);
}
void merge(int x,int y){
	int fx=getfather(x),fy=getfather(y);
	if (fx!=fy) fa[fx]=fy;
}
void add(int x,int y){
nextt[++tot]=lk[x];lk[x]=tot;son[tot]=y;
}
int main(){
	freopen("compression.in","r",stdin);
	freopen("compression.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) scanf("%d",&map[g(i,j)]);
	for (int i=1;i<=n*m;i++) fa[i]=i;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++) temp[j].first=map[g(i,j)],temp[j].second=j;
		sort(temp+1,temp+1+m);
		for (int j=1;j<m;j++) if (temp[j].first==temp[j+1].first) merge(g(i,temp[j].second),g(i,temp[j+1].second));
	}
	for (int i=1;i<=m;i++){
		for (int j=1;j<=n;j++) temp[j].first=map[g(j,i)],temp[j].second=j;
		sort(temp+1,temp+1+n);
		for (int j=1;j<n;j++) if (temp[j].first==temp[j+1].first) merge(g(temp[j].second,i),g(temp[j+1].second,i));
	}
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++) temp[j].first=map[g(i,j)],temp[j].second=j;
		sort(temp+1,temp+1+m);
		for (int j=1;j<m;j++) if (temp[j].first<temp[j+1].first) add(getfather(g(i,temp[j].second)),getfather(g(i,temp[j+1].second)));
	}
	for (int i=1;i<=m;i++){
		for (int j=1;j<=n;j++) temp[j].first=map[g(j,i)],temp[j].second=j;
		sort(temp+1,temp+1+n);
		for (int j=1;j<n;j++) if (temp[j].first<temp[j+1].first) add(getfather(g(temp[j].second,i)),getfather(g(temp[j+1].second,i)));
	}
	for (int i=1;i<=tot;i++) D[son[i]]++;
	for (int i=1;i<=n*m;i++) if (getfather(i)==i && D[i]==0) que[++tail]=i,V[i]=1;
	while (head<tail){
		int x=que[++head];
		for (int i=lk[x];i;i=nextt[i]){
			V[son[i]]=max(V[son[i]],V[x]+1);
			D[son[i]]--;
			if (D[son[i]]==0) que[++tail]=son[i];
		}
	}
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++) printf("%d ",V[getfather(g(i,j))]);
		printf("\n");
	}
	return 0;
}

【写的有漏洞的,欢迎路过大神吐槽】

  2016-11-7 20:11:50

 Ending.

<think>好的,我现在需要帮助用户解决关于有向无环图(DAG)最长通路算法的问题。首先,我得回忆一下DAG的最长路径问题的基础知识。用户提到了拓扑排序和动态规划,这可能是一个关键点。 根据引用[2],DAG的最长路径问题可以通过拓扑排序和动态规划来解决。虽然一般有向图的最长路径是NP难的,但DAG有线性时间的解法。这应该是因为拓扑排序后,节点的处理顺序确保了无环,从而可以使用动态规划。 接下来,我需要确认具体的算法步骤。根据引用中的信息,拓扑排序后,按照拓扑序处理每个节点,更新其相邻节点的最长路径值。每个节点的dist[v]表示从起点到v的最长路径,初始时可能需要设置为负无穷,起点设为0或者根据具体情况调整。 用户可能需要代码示例,所以我要考虑使用哪种语言。引用[1]提到了C++,但用户没有指定语言,可能Python更通用。不过,用户提供的示例中有Python代码,所以用Python可能更合适。 然后,我需要考虑如何实现拓扑排序。Kahn算法或者DFS都可以。Kahn算法通过入度表来排序,而DFS通过递归栈。这里可能需要选择一种方法。Kahn算法可能更直观,适合动态规划的处理。 接下来,动态规划部分。对于每个节点u,遍历其所有邻居v,如果dist[v] < dist[u] + weight(u, v),则更新dist[v]。这需要邻接表的表示,可能用字典或者列表来存储图的结构。 另外,引用[3]提到处理负权重边的情况,DAG的最短路径算法可以处理负权重,但最长路径可能不需要特别处理负权重,因为动态规划方法本身可以处理。不过最长路径中如果有正权重环的话,但DAG不存在环,所以没问题。 需要考虑用户是否需要具体的实现步骤,比如图的构建、拓扑排序的实现、动态规划的应用,以及如何处理多个起点的情况。比如,如果用户需要找到全局最长路径,可能需要将所有节点的dist初始化为负无穷,然后对每个节点作为起点运行算法?不过这样时间复杂度会很高。正确的做法是,在拓扑排序后,遍历所有可能的起点,或者直接在整个拓扑序列中处理,找到最大的dist值。 可能用户的问题是如何实现整个算法,所以需要给出具体步骤:构建图、拓扑排序、初始化dist数组、遍历拓扑序更新距离、最后找到最大值。 最后,生成相关问题,比如时间复杂度、如何处理特定情况、比较其他算法等,以帮助用户深入理解。 总结步骤: 1. 图的表示(邻接表) 2. 拓扑排序(Kahn算法或DFS) 3. 动态规划计算最长路径 4. 寻找所有节点中的最大值 需要确保代码正确,比如处理入度表和队列,以及更新距离的方式。同时,需要考虑权重是否为负数,但根据问题描述,可能默认权重可以是任意值,只要没有环。</think>### 有向无环图(DAG)最长通路算法实现 有向无环图(DAG)的最长通路问题可通过**拓扑排序+动态规划**解决,时间复杂度为$O(V+E)$,其中$V$为顶点数,$E$为边数[^2]。以下是具体实现步骤: --- #### **1. 算法步骤** 1. **拓扑排序** 生成顶点的线性序列,保证所有有向边从序列前驱指向后继。 2. **动态规划** 按拓扑序遍历顶点,用$dist[v]$记录从任意起点到$v$的最长路径长度: $$dist[v] = \max_{(u,v) \in E} \{dist[u] + weight(u,v)\}$$ 初始时$dist[v] = 0$(若需全局最长路径,需遍历所有起点)。 --- #### **2. Python实现代码** ```python from collections import deque def longest_path_dag(vertices, edges): # 初始化邻接表和入度表 adj = {v: [] for v in vertices} in_degree = {v: 0 for v in vertices} for u, v, w in edges: adj[u].append((v, w)) in_degree[v] += 1 # 拓扑排序(Kahn算法) queue = deque([v for v in vertices if in_degree[v] == 0]) topo_order = [] while queue: u = queue.popleft() topo_order.append(u) for v, _ in adj[u]: in_degree[v] -= 1 if in_degree[v] == 0: queue.append(v) # 动态规划计算最长路径 dist = {v: 0 for v in vertices} # 若允许负权重,初始化为负无穷 for u in topo_order: for v, w in adj[u]: if dist[v] < dist[u] + w: dist[v] = dist[u] + w return max(dist.values()) if dist else 0 # 示例调用 vertices = ['A', 'B', 'C', 'D'] edges = [('A', 'B', 3), ('A', 'C', 2), ('B', 'D', 5), ('C', 'D', 4)] print("最长路径长度:", longest_path_dag(vertices, edges)) # 输出:3+5=8(A→B→D) ``` --- #### **3. 关键点说明** - **拓扑排序必要性**:确保动态规划按无环顺序更新,避免循环依赖[^2]。 - **负权重处理**:若边权可为负,初始化$dist[v]$为负无穷,并将起点设为0。 - **全局最长路径**:需遍历所有可能的起点,或直接取$dist$中的最大值(如示例所示)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值