【前缀和 BFS 并集查找】P3127 [USACO15OPEN] Trapped in the Haybales G|省选-

本文涉及知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
C++DFS
C++并集查找

P3127 USACO15OPEN Trapped in the Haybales G

题目描述

Farmer John 收到了一批 N N N 个大型干草捆( 1 ≤ N ≤ 100 , 000 1 \le N \le 100,000 1≤N≤100,000),并将它们放置在他通往谷仓的道路上的不同位置。不幸的是,他完全忘记了奶牛 Bessie 正在这条路上吃草,她现在可能被困在这些干草捆之间了!每个干草捆 j j j 有一个大小 S j S_j Sj 和一个位置 P j P_j Pj,表示它在这条一维道路上的位置。Bessie 可以在道路上自由移动,甚至可以移动到干草捆所在的位置,但她无法穿过这个位置。唯一的例外是,如果她朝同一方向连续移动 D D D 单位的距离,她将获得足够的速度,能够突破并永久消除任何大小严格小于 D D D 的干草捆。当然,在突破之后,她可能会打开更多的空间,从而有机会突破其他干草捆,并继续消除它们。

如果 Bessie 最终能够突破最左侧或最右侧的干草捆,她就可以成功逃脱。请计算道路中所有无法逃脱的实数起始位置的总面积。

输入格式

输入的第一行包含 N N N。接下来的 N N N 行描述每个干草捆,每行包含两个整数,分别表示干草捆的大小和位置,范围均为 1 ... 1 0 9 1 \ldots 10^9 1...109。所有位置均不相同。

输出格式

输出一个整数,表示 Bessie 无法逃脱的道路总面积。

输入输出样例 #1

输入 #1

复制代码
5
8 1
1 4
8 8
7 15
4 20

输出 #1

复制代码
14

BFS( 错误 错误 错误)

性质一 :n堆干草,将整个道路分成n-1区间。任何区间,只要有一个点能够突破所有干草堆,则整个区间能突破所有干草堆。以向右突破为例,本区间所有点先向左到左端点后,再向右。
0 ≤ i ≤ N − 2 0\leq i \le N-2 0≤i≤N−2 ri记录到达第i堆干草至少已经向右ri,才能向右突破所有草堆。

ri =max(ri+1-此区间长度,si-此区间长度)

如果ri<0,可以做起点。

类似:lefti记录到达第i+1堆干草,至少已经向左lefti,才能向左突破所有草堆。

如果区间lefti<0或ri<0,则称此区间能够直接突破。如果没有区间能直接突破,则所有区间不能突破。
性质二 :区间一和区间二相邻,如果区间一为起点能够到达区间二,则区间二能够突破所有干草,则区间一也能。即一个连通区域只要有一个点能突破,则连通区域所有点都能突破。注意 :相邻是有向的。

实现:边反向后,以直接突破的区间为起点,BFS。
错误原因 :存在以下情况: 区间一 → 区间二 → 区间三 区间一\rightarrow 区间二 \rightarrow 区间三 区间一→区间二→区间三,但区间二无法到达区间三。

暴力解法:枚举所有边而不是相邻边。时间复杂度:O(nn) 时间超限。

BFS+虚拟节点+前缀和(错误)

根据上述分析,我们可以得出如下结论。
性质一 :一个区间可以缩成一个点,共n-1个点,增加一个虚拟节点n-1,共n个节点。
性质二 :所有能直接突破的区间对应的点连向n-1。

如果是BFS,不需要虚拟节点。

增加区间间的边

以向右的边( i < j < k i<j<k i<j<k)为例。从i能够到达k,且不能到达k+1。向左的边类似。
性质三 :如果j能够到达k,则只需要 i → j → k i \rightarrow j \rightarrow k i→j→k,无需 i → k i \rightarrow k i→k
性质四 :如果i不能到达j,则i也无法到达k。
小结一 :如果 j → k j \rightarrow k j→k,则除j外,不需要任意其它边连向k。
性质五 :如果i不能到达 k + 1 k+1 k+1,则一定不能到达 k + 2 k+2 k+2。

栈sta记录所有 ∀ j \forall j ∀j都不能到达的k,从栈底到栈顶降序。当i能到达栈顶,连边出栈。

判断i能否到达栈顶:

区间i到栈顶的距离 是否大于 栈顶S。
错误原因

区间一无法到达区间二,区间二无法直接到达区间三,但 区间 2 → 区间 1 → 区间 3 区间2 \rightarrow 区间1 \rightarrow 区间3 区间2→区间1→区间3却成立。

因为干草堆是永远消失。

错误代码

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 <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 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();
		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 Solution {
		public:
			long long Ans(vector<pair<int, int>>& sp) {			
				const int N = sp.size();
				sort(sp.begin(), sp.end(), [&](const auto& p1, const auto& p2) {return p1.second < p2.second; });
				vector<long long >left(N ), r(N ),vLen(N);
				for (int i = N - 2; i >= 0; i--) {
					vLen[i] = sp[i + 1].second - sp[i].second;
					r[i] = max((long long)sp[i + 1].first, r[i + 1] ) - vLen[i];
				}
				vector<long long> preSum = { 0 };
				for (const auto& i : vLen) {
					preSum.emplace_back(preSum.back() + i);
				}
				long long ans = 0;
				queue<int> que;
				vector<bool> vis(N - 1);
				auto Add = [&](int cur) {
					if (vis[cur]) { return; }
					vis[cur] = true;
					que.emplace(cur);
					ans -= vLen[cur];
				};
				for (int i = 0; i + 1 < N; i++) {	
					if (0 == i) {
						left[i] = sp[i].first - vLen[i];
					}
					else {
						left[i] = max( (long long)sp[i].first,left[i-1]) - vLen[i];
					}
					if ((left[i] < 0) || (r[i] < 0)) { Add(i); }
				}
				vector<vector<int>> neiBoBack(N - 1);				
				{//处理向右的边
					stack<int> sta;
					for (int i = N - 2; i >= 0; i--) {
						while (sta.size()&&( preSum[sta.top()]- preSum[i] > sp[sta.top()].first))
						{
							neiBoBack[sta.top()].emplace_back(i);
							sta.pop();
						}
						sta.emplace(i);
					}
				}
				{//处理向左的边
					stack<int> sta;
					for (int i = 0; i + 1 < N; i++) {
						while(sta.size()&&(preSum[i+1]-preSum[sta.top()+1] > sp[sta.top()+1].first))
						{
							neiBoBack[sta.top()].emplace_back(i);
							sta.pop();
						}
						sta.emplace(i);
					}
				}
				while (que.size()) {
					const auto cur = que.front(); que.pop();
					for (const auto&next : neiBoBack[cur]) {
						Add(next);
					}
				}
				return ans+ preSum.back();
			}
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	ios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);
	auto sp = Read<pair<int, int>>();	
#ifdef _DEBUG
	//printf("N=%d", N);
	Out(sp, ",sp=");
	//Out(que, ",=que");
#endif // DEBUG
	auto res = Solution().Ans(sp);
	cout << res << "\n";
	return 0;
}

并集查找

令某区间i,不转向向左能到left,向右能到r。此时left到r之间的干草堆已经被消除,故可以看成一个大区间。

left,r不转向最左能够到达left1,最右能够到达r1。如果 l e f t = = l e f t 1 且 r = = r 1 left == left1 且 r == r1 left==left1且r==r1则迭代结束。

否则 l e f t = l e f t 1 , r = r 1 left=left1,r=r1 left=left1,r=r1继续迭代。

如果最终 l e f t < 0 或 r > N − 2 left < 0或r >N-2 left<0或r>N−2则区间i能够突破所有。
性质一 :如果i不能突破,则i,r都不能突破。区间i到达区间k 速度 ≥ 0 速度\ge0 速度≥0,从区间k出发速度0。
性质二:同理i能到达j,j能到达的区间,则i也能到达。leftj记录j能到达的最左点,如果i能到达j,则能到达leftj ,leftleft\[j],我们用并集查找记录。

超时代码

c 复制代码
	class Solution {
		public:
			long long Ans(vector<pair<int, int>>& sp) {
				const int N = sp.size();
				sort(sp.begin(), sp.end(), [&](const auto& p1, const auto& p2) {return p1.second < p2.second; });
				vector<long long > vLen(N);
				for (int i = N - 2; i >= 0; i--) {
					vLen[i] = sp[i + 1].second - sp[i].second;
				}
				vector<long long> preSum = { 0 };
				for (const auto& i : vLen) {
					preSum.emplace_back(preSum.back() + i);
				}

				CUnionFindDirect uf(N);
				bool b = false;
				auto Check = [&](int x) {
					int left = x, r = x + 1;
					while (left >= 0)
					{
						const long long len = preSum[r] - preSum[left];
						if (len > sp[left].first) {//可以向左
							if (0 == left) { break; }
							left = uf.GetMaxDest(left - 1);
							if (left == N - 1) { break; }
						}
						else if (len > sp[r].first) {//可以向右
							r++;
							if (r == N) { break; }
						}
						else {
							uf.Connect(b, b, x, left);
							return false;
						}
					}
					uf.Connect(b, b, x, N - 1);
					return true;
				};
				long long ans = 0;
				for (int i = 0; i + 1 < N; i++) {
					Check(i);
					if (N - 1 != uf.GetMaxDest(i)) {
						ans += vLen[i];
					}
				}
				return ans;
			}
		};

解决方法

如果区间i无法突破,则i+1...r都无法突破,flagi+1...r= true,如果flagi之间 ans += vLeni并忽略。

除r++ 外的时间复杂度接近:O ( N ) (N) (N)

r++的时间复杂度估计时:O(N)

代码

核心代码

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 <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 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();
		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 CUnionFindDirect
{
public:
	CUnionFindDirect(int iSize)
	{
		m_vRoot.resize(iSize);
		iota(m_vRoot.begin(), m_vRoot.end(), 0);
	}
	void Connect(bool& bConflic, bool& bCyc, int iFrom, int iTo)
	{
		bConflic = bCyc = false;
		if (iFrom != m_vRoot[iFrom])
		{
			bConflic = true;
		}

		Fresh(iTo);
		if (m_vRoot[iTo] == iFrom)
		{
			bCyc = true;
		}
		if (bConflic || bCyc)
		{
			return;
		}

		m_vRoot[iFrom] = m_vRoot[iTo];
	}
	int GetMaxDest(int iFrom)
	{
		Fresh(iFrom);
		return m_vRoot[iFrom];
	}
private:
	int Fresh(int iNode)
	{
		if (m_vRoot[iNode] == iNode)
		{
			return iNode;
		}
		return m_vRoot[iNode] = Fresh(m_vRoot[iNode]);
	}
	vector<int> m_vRoot;
};
class Solution {
public:
	long long Ans(vector<pair<int, int>>& sp) {
		const int N = sp.size();
		sort(sp.begin(), sp.end(), [&](const auto& p1, const auto& p2) {return p1.second < p2.second; });
		vector<long long > vLen(N);
		for (int i = N - 2; i >= 0; i--) {
			vLen[i] = sp[i + 1].second - sp[i].second;
		}
		vector<long long> preSum = { 0 };
		for (const auto& i : vLen) {
			preSum.emplace_back(preSum.back() + i);
		}

		CUnionFindDirect uf(N);
		bool b = false;
		vector<bool> flag(N);
		auto Check = [&](int x) {
			int left = x, r = x + 1;
			while (left >= 0)
			{
				const long long len = preSum[r] - preSum[left];
				if (len > sp[left].first) {//可以向左
					if (0 == left) { break; }
					left = uf.GetMaxDest(left - 1);
					if (left == N - 1) { break; }
				}
				else if (len > sp[r].first) {//可以向右
					r++;
					if (r == N) { break; }
				}
				else {
					for (; x < r; x++)
					{
						uf.Connect(b, b, x, left);
						flag[x] = true;
					}
					return false;
				}
			}
			uf.Connect(b, b, x, N - 1);
			return true;
		};
		long long ans = 0;
		for (int i = 0; i + 1 < N; i++) {
			if (flag[i]) {
				ans += vLen[i];
				continue;
			}
			Check(i);
			if (N - 1 != uf.GetMaxDest(i)) {
				ans += vLen[i];
			}
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	ios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);
	auto sp = Read<pair<int, int>>();
#ifdef _DEBUG
	//printf("N=%d", N);
	Out(sp, ",sp=");
	//Out(que, ",=que");
#endif // DEBUG
	auto res = Solution().Ans(sp);
	cout << res << "\n";
	return 0;
}

单元测试

cpp 复制代码
vector<pair<int, int>> sp;
		TEST_METHOD(TestMethod11)
		{
			sp = { {8,1},{1,4},{8,8},{7,15},{4,20} };
			auto res = Solution().Ans(sp);
			AssertEx(14LL, res);
		}
		TEST_METHOD(TestMethod12)
		{
			sp = { {1000,1},{4,101},{3,103 },{1,105} };//区间长度100,[1000,4] 2 [4,3] 2[3,1]
			auto res = Solution().Ans(sp);
			AssertEx(2LL, res);
		}

疑问

感觉下面代码有问题:

cpp 复制代码
for (; x < r; x++)
					{
						uf.Connect(b, b, x, left);
						flag[x] = true;
					}

改成这样也能通过,且容易理解。

cpp 复制代码
  uf.Connect(b, b, x, left);
					for (; x < r; x++)
					{						
						flag[x] = true;
					}

扩展阅读

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

视频课程

先学简单的课程,请移步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++**实现。

相关推荐
凡人叶枫8 分钟前
Effective C++ 条款22:将成员变量声明为 private
linux·开发语言·c++
坚果派·白晓明2 小时前
【鸿蒙PC】SDL3 移植:AtomCode Skills 4 步速通多媒体库适配
c++·华为·ai编程·harmonyos·atomcode·c/c++三方库
赴生-2 小时前
C++进阶 C++11(下)
开发语言·c++
有点。3 小时前
C++(贪心算法一)
c++·贪心算法
WBluuue3 小时前
数据结构与算法:有序表(二):跳表
数据结构·c++·算法·skiplist
赴生-3 小时前
C++进阶 异常
开发语言·c++
不好听6134 小时前
深入理解链表:线性数据结构的另一面
javascript·数据结构
凡人叶枫4 小时前
Effective C++ 条款28:避免使用 handles 指向对象内部
linux·服务器·开发语言·c++·嵌入式开发
zwenqiyu5 小时前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法
Queenie_Charlie5 小时前
哈夫曼树
数据结构·c++·哈夫曼树