• P3387 【模板】缩点 TJ


    题目链接

    思路

    模板题,先使用 (Tarjan) 进行缩点,然后重新建边,最后拓扑排序即可

    Tarjan

    强连通分量

    在有向图G<V,E>中,对于点集V'∈V, 点集中的任意两点都可达,则称V'为强连通

    Tarjan

    在有向图中,(DFS) 时,我们需要维护 (dfn)(low),目的在于把所有的强连通分量缩成一个点(因为题目里说可以重复走,而强连通分量里两点都可以到达)
    (dfn_i) 为遍历到当前节点 (i) 的时间戳,(low_i) 为当前节点 (i) 能回溯到的最早节点,
    (dfn_i = low_i) 时说明以 (i) 为根形成了强连通分量,
    此时我们可以用栈来维护当前强连通分量里的所有点。

    拓扑排序

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。

    此时缩点后的图是一个有向无环图(强连通分量被缩成点),我们考虑使用拓扑排序求出最终的最长路径
    先将入度为 (0) 的点进入队列,然后出队,将可到达的点的入度减 (1) ,然后将当前入度为 (0) 点加入队列,过程中可以 (DP) 求出最长路径

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    const int MAXN = 1e4 + 10;
    const int MAXM = 1e5 + 10;
    queue <int > s;
    
    int sta[MAXN] ,ind = 0;
    
    vector <int > c[MAXN];
    int in[MAXN];
    
    struct node {
    	int next, to ,from;
    }edge[MAXM];
    int head[MAXN] ,cnt = 0;
    void add (int from ,int to) {
    	edge[++ cnt].next = head[from];
    	head[from] = cnt;
    	edge[cnt].to = to;
    	edge[cnt].from = from;
    }
    
    int n ,m ,answ = 0;
    int dis[MAXN] ,dis_[MAXN];
    
    int tot = 0 ,times = 0;
    int dfn[MAXN] ,low[MAXN] ,ins[MAXN] = {0} ,vis[MAXN];
    void tarjan (int u) {
    	dfn[u] = low[u] = ++ times;//初始dfn和low均为时间戳 
    	sta[++ ind] = u ,ins[u] = 1;//进栈,手写 
    	for (int q = head[u];~ q;q = edge[q].next) {//dfs 
    		int v = edge[q].to;
    		if (! dfn[v]) {//向下遍历 
    			tarjan (v);
    			low[u] = min (low[u] ,low[v]);
    		}
    		else if (ins[v])
    			low[u] = min (low[u] ,dfn[v]);
    	}
    	if (low[u] == dfn[u]) {//找到强连通分量 
    		tot ++;//当前强连通分量 的 新的节点编号(因为缩成一个点了)
    		while (true) {
    			vis[sta[ind]] = tot;
    			dis_[tot] += dis[sta[ind]];//将路径长度累加到点上 
    			ins[sta[ind]] = 0; ind --;//出栈 
    			if (u == sta[ind + 1])//当前到根,退出 
    				break;
    		}
    	}
    }
    int tot_ = 0;
    int dp[MAXN];
    void toposort () {
    	for (int q = 1;q <= tot;++ q) {//预处理入度为0的节点 
    		if (in[q] == 0) {
    			s.push(q);
    			dp[q] = dis_[q];
    		}
    	}
    	while (! s.empty()) {
    		int u = s.front();
    		s.pop();
    		for (int q = 0;q < c[u].size();++ q) {//向下遍历 
    			int v = c[u][q];
    			in[v] --;//减去1 
    			dp[v] = max (dp[v] ,dp[u] + dis_[v]);//DP求出最长路 
    			if (in[v] == 0) {//将当前入度为0的节点进队 
    				s.push(v);
    				answ = max (answ ,dp[v]);
    			}
    		}
    	}
    }
    
    int main () {
    	memset (head ,-1 ,sizeof (head));
    	memset (in ,0 ,sizeof (in));
    	scanf ("%d%d",&n ,&m);
    	for (int q = 1;q <= n;++ q)
    		scanf ("%d",dis + q);
    	int from_ ,to_;
    	for (int q = 1;q <= m;++ q) {//初始边,此处链式前向星 
    		scanf ("%d%d",&from_ ,&to_);
    		add (from_ ,to_);
    	}
    	for (int q = 1;q <= n;++ q)//tarjan
    		if (! dfn[q]) tarjan (q);
    	for (int q = 1;q <= m;++ q) {//重新建边(缩点后) ,此处用邻接表 
    		if (vis[edge[q].from] != vis[edge[q].to]) {
    			from_ = vis[edge[q].from] ,to_ = vis[edge[q].to];
    			c[from_].push_back(to_);
    			in[to_] ++;
    		}
    	}
    	toposort ();//拓扑排序 
    	for (int q = 1;q <= tot;++ q)
    		answ = max (answ ,dp[q]);
    	printf ("%d
    ",answ);
    	return 0;
    }
    
    
    cb
  • 相关阅读:
    《Java技术》第一次作业
    第十一次作业
    第十次作业
    第九次作业。
    第八次作业
    第七次作业
    第六次作业
    第五次作业。
    《Java技术》第三次作业--面向对象——继承、抽象类、接口
    《Java技术》第二次作业--面向对象基础
  • 原文地址:https://www.cnblogs.com/tuscjaf/p/13831018.html
Copyright © 2020-2023  润新知