【强连通分量 缩点 拓扑排序】P3387 【模板】缩点|普及+

本文涉及知识点

C++图论 强连通分量 拓扑排序

P3387 【模板】缩点

题目描述

给定一个 n n n 个点 m m m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入格式

第一行两个正整数 n , m n,m n,m。

第二行 n n n 个整数,其中第 i i i 个数 a i a_i ai 表示点 i i i 的点权。

第三至 m + 2 m+2 m+2 行,每行两个整数 u , v u,v u,v,表示一条 u → v u\rightarrow v u→v 的有向边。

输出格式

共一行,最大的点权之和。

输入输出样例 #1

输入 #1

复制代码
2 2
1 1
1 2
2 1

输出 #1

复制代码
2

说明/提示

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 10 4 1\le n \le 10^4 1≤n≤104, 1 ≤ m ≤ 10 5 1\le m \le 10^5 1≤m≤105, 0 ≤ a i ≤ 10 3 0\le a_i\le 10^3 0≤ai≤103。

P3387 【模板】缩点

强连通分量 缩点 拓扑排序

强联通分量缩成一点,点权等于强连通分量所有节点权之和。

缩点无环,按拓扑序处理各节点u,可以保证无后效性。

v是u的任意临接点 dp[u] = u的权值 + max(dp[v])

代码

核心代码

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) >> get<6>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& [u, v, w] : edges)
		{
			vNeiBo[u - iBase].emplace_back(v - iBase, w);
			if (!bDirect)
			{
				vNeiBo[v - iBase].emplace_back(u - iBase, w);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

class CBFSLeve {
public:
	static vector<int> Leve(const vector<vector<int>>& neiBo, vector<int> start) {
		vector<int> leves(neiBo.size(), -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			for (const auto& next : neiBo[start[i]]) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	template<class NextFun>
	static vector<int> Leve(int N, NextFun nextFun, vector<int> start) {
		vector<int> leves(N, -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			auto nexts = nextFun(start[i]);
			for (const auto& next : nexts) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	static vector<vector<int>> LeveNodes(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> ret(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			ret[leves[i]].emplace_back(i);
		}
		return ret;
	};
	static vector<int> LeveSort(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> leveNodes(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			leveNodes[leves[i]].emplace_back(i);
		}
		vector<int> ret;
		for (const auto& v : leveNodes) {
			ret.insert(ret.end(), v.begin(), v.end());
		}
		return ret;
	};
};

class CSCCTarjan {
public:
	CSCCTarjan(vector<vector<int>>& neiBo) :m_neiBo(neiBo) {
		const int N = neiBo.size();
		m_vTime.assign(N, -1);
		m_vBack.assign(N, -1);
		m_vIsStack.assign(N, false);
		for (int i = 0; i < N; i++) {
			DFS(i);
		}
	}
	void InitPtNew() {
		m_ptNew.resize(m_neiBo.size());
		iota(m_ptNew.begin(), m_ptNew.end(), 0);
		for (auto& v : m_sccs) {
			nth_element(v.begin(), v.begin(), v.end());
			m_v0.emplace_back(v[0]);
			for (int i = 1; i < v.size(); i++) {
				m_ptNew[v[i]] = v[0];
			}
		}
	}
	vector<vector<int>> GetNewNeiBo() {
		vector<vector<int>> neiBo(m_neiBo.size());
		for (int i = 0; i < neiBo.size(); i++) {
			const int n1 = m_ptNew[i];
			for (const auto& next : m_neiBo[i]) {
				const int n2 = m_ptNew[next];
				if (n1 == n2) { continue; }//自环
				neiBo[n1].emplace_back(n2);
			}
		}
		return neiBo;
	}
	vector<vector<int>> m_sccs;
	vector<int> m_v0, m_ptNew;
protected:
	void DFS(int cur) {
		if (-1 != m_vTime[cur]) { return; }
		m_vTime[cur] = m_vBack[cur] = m_iTimes++;
		m_vIsStack[cur] = true;
		m_sta.emplace(cur);
		for (const auto& next : m_neiBo[cur]) {
			if (-1 == m_vTime[next]) {
				DFS(next);
				m_vBack[cur] = min(m_vBack[cur], m_vBack[next]);
			}
			else if (m_vIsStack[next]) {
				m_vBack[cur] = min(m_vBack[cur], m_vTime[next]);
			}
		}
		if (m_vTime[cur] != m_vBack[cur]) { return; }
		vector<int> scc;
		while (m_sta.size())
		{
			auto u = m_sta.top(); m_sta.pop();
			scc.emplace_back(u);
			m_vIsStack[u] = false;
			if (cur == u) { break; }
		}
		m_sccs.emplace_back(scc);
	}
	vector<vector<int>>& m_neiBo;
	int  m_iTimes = 0;
	vector<int> m_vTime, m_vBack;
	vector<bool> m_vIsStack;
	stack<int> m_sta;
};

class CDGTopSort
{
public:
	template <class T = vector<int> >
	CDGTopSort(const vector<T>& vNeiBo) :m_vDeg(vNeiBo.size()), m_neiBo(vNeiBo) {
		const int N = vNeiBo.size();
		m_backNeiBo.resize(N);
		for (int cur = 0; cur < N; cur++)
		{
			m_vDeg[cur] = vNeiBo[cur].size();
			for (const auto& next : vNeiBo[cur])
			{
				m_backNeiBo[next].emplace_back(cur);
			}
		}
	}
	void Init() {

		auto Add = [&](int i) {
			if (0 != m_vDeg[i]) { return; }
			m_que.emplace(i);
		};
		for (int i = 0; i < m_vDeg.size(); i++)
		{
			Add(i);
		}
		while (m_que.size())
		{
			const int cur = m_que.front(); m_que.pop();
			if (!OnDo(cur)) { continue; }
			for (const auto& next : m_backNeiBo[cur])
			{
				m_vDeg[next]--;
				Add(next);
			}
		};
	}
	queue<int> m_que;
	vector<int> m_vDeg;
	vector<int> m_vSort;
protected:
	const vector<vector<int>>& m_neiBo;
	vector<vector<int>> m_backNeiBo;
	virtual bool OnDo(int cur) {
		m_vSort.emplace_back(cur);
		return true;
	};
};
class Solution {
public:
	int Ans(vector<int>& ws, vector<pair<int, int>>& edge) {
		const int N = ws.size();
		auto neiBo = CNeiBo::Two(N, edge, true, 1);
		CSCCTarjan scc(neiBo);
		scc.InitPtNew();
		for (auto& v : scc.m_sccs) {
			for (int i = 1; i < v.size(); i++) {
				ws[v[0]] += ws[v[i]];
			}
		}
		auto neiBo1 = scc.GetNewNeiBo();
		CDGTopSort ts(neiBo1);
		ts.Init();
		vector<int> ans = ws;
		for (const auto& cur : ts.m_vSort) {
			for (const auto& child : neiBo1[cur])
			{
				ans[cur] = max(ans[cur], ans[child] + ws[cur]);
			}
		}
		const int iMax = *max_element(ans.begin(), ans.end());
		return iMax;
	}
};
int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	//CInBuff<> in; COutBuff<10'000'000> ob;
	int N,M;
	cin >> N >> M;
	auto ws = Read<int>(N);
	auto edge = Read<pair<int, int>>(M);
#ifdef _DEBUG	
		//printf("N=%d",n);
		Out(ws, ",ws=");
		Out(edge, ",edge=");
#endif // DEBUG	
		auto res = Solution().Ans(ws,edge);
		cout << res << "\n";
	return 0;
};

单元测试

cpp 复制代码
vector<int> ws;
		vector<pair<int, int>> edge;
		TEST_METHOD(TestMethod01)
		{
			ws = { 1,1 }, edge = { {1,2},{2,1} };
			auto res = Solution().Ans(ws,edge);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod02)
		{
			ws = { 970,369,910,889,470,106,658,659,916,964 }, edge = { {3,2},{3,6},{3,4},{9,5},{8,3
},{5,8},{9,1},{9,7},{9,8},{7,5},{3,7},{7,8},{1,7},{10,2},{1,10},{4,8},{2,6},{3,1},{3,5},{8,5} };
			auto res = Solution().Ans(ws, edge);
			AssertEx(6911, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法 用**C++**实现。

相关推荐
ideaout技术团队10 分钟前
leetcode学习笔记2:多数元素(摩尔投票算法)
学习·算法·leetcode
代码充电宝14 分钟前
LeetCode 算法题【简单】283. 移动零
java·算法·leetcode·职场和发展
不枯石3 小时前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石3 小时前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森5 小时前
C语言作用域与数组详解
java·数据结构·算法
想唱rap5 小时前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡6 小时前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿6 小时前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
保持低旋律节奏7 小时前
CPP——OJ试题,string、vector、类(题三)初步应用
c++
君生我老7 小时前
C++ string类常用操作
c++