【图论 树 启发式合并】P7165 [COCI2020-2021#1] Papričice|普及+

本问涉及知识点

C++图论 树 启发式合并

[COCI2020-2021#1] Papričice

题目描述

给定一个 n n n 个点的树,这 n n n 个点编号为 1 1 1 到 n n n。

现在要选择断掉两条边,会形成三个连通块,假设这三个连通块内的点数分别为 a , b , c a,b,c a,b,c,那么您要做的就是最小化 max ⁡ { a , b , c } − min ⁡ { a , b , c } \max\{a,b,c\}-\min\{a,b,c\} max{a,b,c}−min{a,b,c} 的大小,求这个最小值。

输入格式

第一行一个整数 n n n 代表树的点数。

接下来 n − 1 n-1 n−1 行每行两个整数 x , y x,y x,y 代表树的一条边。

输出格式

一行一个整数代表答案。

样例 #1

样例输入 #1

复制代码
4
1 2
2 3
3 4

样例输出 #1

复制代码
1

样例 #2

样例输入 #2

复制代码
6
1 2
1 3
3 4
3 5
5 6

样例输出 #2

复制代码
0

样例 #3

样例输入 #3

复制代码
9
1 3
2 3
3 4
3 5
5 6
5 7
7 8
7 9

样例输出 #3

复制代码
2

提示

样例 1 解释

能构造的最优解三个连通块的点数都为 1 , 1 , 2 1,1,2 1,1,2,所以输出 2 − 1 = 1 2-1=1 2−1=1。

样例 2 解释

断掉点 1 1 1 到点 3 3 3 的边,点 3 3 3 到点 5 5 5 的边,形成的三个连通块点数相同。

样例 3 解释

如下图所示:

数据规模与约定

本题采用捆绑测试。

  • Subtask 1(15 pts): 3 ≤ n ≤ 200 3 \le n \le 200 3≤n≤200。
  • Subtask 2(35 pts): 3 ≤ n ≤ 2000 3 \le n \le 2000 3≤n≤2000。
  • Subtask 3(60 pts): 3 ≤ n ≤ 2 × 10 5 3 \le n \le 2 \times 10^5 3≤n≤2×105。

对于 100 % 100\% 100% 的数据, 1 ≤ x , y ≤ n 1 \le x,y \le n 1≤x,y≤n。

本题满分 110 110 110 分。

说明

翻译自 Croatian Open Competition in Informatics 2020 ~ 2021 Round 1 D Papričice

图论 树 启发式合并

O(nn)

n-1条边都是某节点指向父节点,且除根节点外,都有且只有一条边指向父节点。

一,建立临接表。

二,通过临接表以0为根,BFS出各节点层次及各层次节点。

三,求各节点的父节点。

四,通过父节点和层次初始化树上倍增。

五,求各节点的大小s。

六,枚举两个节点a,b,令其公共祖先是c。

如果a的层次大于b,忽略。

如果c=a ,三个数为:s(0)-s(a),s(b) ,s(a)-s(b)

否则,三个数是 s(0)-s(a)-s(b),s(a),s(b)

分治发 O(nlognlogn)(错误)

启发性合并:当前节点和后代最多的子节点互换,复制其它子节点的后代。时间复杂度:O(nlogn)。 任意节点cur顶多复制logn次。第一次复制后,cur所在子树的节点数至少2,第二次复制后,cur所在子树的节点数至少4, ⋯ \cdots ⋯ logn次后,cur所在子树的节点数就达到n了。被复制,说明存在更多后代的兄弟子树,故每次复制cur所在的子树节点数都至少翻倍。

一,a是b的祖先。利用启发式合并用有序集合cc,记录节点a所有后代的大小。无论s(0)-s(a)是最小值,还是最大值,还是中间值。s(a)-s(b)和s(b)越接近越好。

即abs(s(a)-2s(b))最小,如果s(a) >= 2s(b),我们只比较最大s(b);如果s(a) <= 2s(b),我们只比较最小s(b)

如果s(a)是偶数,上面式子等效于 s(b) <= s(a)/2 ,s(b) >= s(a)/2

如果s(a)是奇数s(b) <= s(a)/2 ,则s(a) -2s(b) <=0 ;s(b) > s(a)/2 ,则 s(a)-2s(b) > 0

总之上所述:s(b) <= s(a)/2 ,我们比较最大s(b);s(b) > s(a)/2,我们比较最小s(b)

即 it = cc.upper(s(a)/2),比较it和 (--it)

二,a和b不是祖先、后代的关系。对于给定a,无论s(a)是最大值、中间值、最小值,x1=s(0)-s(a)-s(b)和s(b)都是越接近越好。令x = (s(0)-s(a))/2

如果s(b) <= x 。x1 -s(b) >=0,故s(b)越大越好。如果sb(b) > x,s(b)-x >=0,s(b)越小越好。

计算完各子树节点数后,调整临接表,确保长子的后代数最多。

如果a、b的级别不一样,只枚举级别小。进一步分两种情况:

b是大哥子树的后代,即启发式合并时的父节点。

b是小弟子树的后代,从最年幼的子树开始枚举。由于长子不是任何兄弟的弟弟,故无需枚举。故时间和启动式合并一样O(nlogn)。

正确解法

上述解法错误原因:a的层次小于等于b,则b不一定再a的亲兄弟树。可能在b的堂兄弟树。

leveNodes[i]记录树层次节点, i从大道小,v =leveNodes[i]

先计算v各元素的cc。

确保cnt[v[0]]大于等于ctn[v的其它元素]。

j = 1 to 大

cc[v[0]] 就是v[j] 所有堂兄树

cc[v[0]]加上 cc[v[j]]

for j 从 v.size()-1 到 1

cc[v[0]]减去 cc[v[j]]

tmp += cc[v[j]]

tmp是v[j-1]所有堂弟树

代码

无论如何都超时,于是删除部分代码,看问题在哪?结果通过了。

要么样例太弱,要么级别高的点必定在堂长兄。

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 <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;
	scanf("%d", &n);
	vector<T> ret(n);
	for(int i=0;i < n ;i++) {
		cin >> ret[i];
	}
	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;
}
class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, 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, 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<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 Solution {
		public:
			int Ans(vector<pair<int, int>>& edge) {
				const int N = edge.size() + 1;
				int ans = INT_MAX / 2;
				auto Ans = [&](int a, int b, int c) {
					const int iMax = max(max(a, b), c);
					const int iMin = min(min(a, b), c);
					ans = min(ans, iMax - iMin);
				};
				auto neiBo = CNeiBo::Two(N, edge, false, 1);
				auto leves = CBFSLeve::Leve(neiBo, { 0 });
				auto leveNodes = CBFSLeve::LeveNodes(leves);
				auto leveSorts = CBFSLeve::LeveSort(leves);
				vector<int> vs2(leveSorts.rbegin(), leveSorts.rend());
				vector<vector<int>> childs(N);
				for (int i = 0; i < N; i++) {
					for (const auto& next : neiBo[i]) {
						if (leves[next] < leves[i]) { continue; }
						childs[i].emplace_back(next);
					}
				}
				vector<int> cnt(N, 1);
				for (const auto& cur : vs2) {
					for (const auto& chi : childs[cur]) {
						cnt[cur] += cnt[chi];
					}
				}
				for (auto& v : childs) {
					for (int i = 1; i < v.size(); i++) {
						if (cnt[v[i]] > cnt[v[0]]) { swap(v[i], v[0]); }
					}
				}
				vector<multiset<int>> cc(N);
				auto Do33 = [&N, Ans](multiset<int>& tmp, int ccnt) {
					const int x = (N - ccnt) / 2;
					auto it = tmp.upper_bound(x);
					if (tmp.end() != it) {
						Ans(ccnt, *it, N - ccnt - *it);
					}
					if (tmp.begin() != it) {
						Ans(ccnt, *prev(it), N - ccnt - *prev(it));
					}
				};
				auto Do = [&](int cur) {
					cc[cur].swap(cc[childs[cur][0]]);
					for (int i = 1; i < childs[cur].size(); i++) {
						const auto& chi = childs[cur][i];
						cc[cur].insert(cc[chi].begin(), cc[chi].end());
					}
					auto it = cc[cur].upper_bound(cnt[cur] / 2);
					if (cc[cur].end() != it) {
						Ans(cnt[cur] - *it, *it, N - cnt[cur]);
					}
					if (cc[cur].begin() != it) {
						Ans(cnt[cur] - *prev(it), *prev(it), N - cnt[cur]);
					}
				};
				vector<vector<int>> tv(leveNodes.rbegin(), leveNodes.rend());
				for (auto& v : tv )
				{
					for (int i = 1; i < v.size(); i++) {
						if (cnt[v[i]] > cnt[v[0]]) { swap(v[0], v[i]); }
					}
					for (const auto& cur : v) {
						if (!childs[cur].empty()) { Do(cur); }
						cc[cur].emplace(cnt[cur]);
					}
					for (int i = 1; i < v.size(); i++) {
						Do33(cc[v[0]], cnt[v[i]]);
						//cc[v[0]].insert(cc[v[i]].begin(), cc[v[i]].end());
					}
					/*multiset<int> tmp;
					for (int i = v.size() - 1; i > 0; i--) {
						tmp.insert(cc[v[i]].begin(), cc[v[i]].end());
						Do33(tmp, cnt[v[i - 1]]);
					}
					for (const auto& i : tmp) {
						cc[v[0]].erase(cc[v[0]].find(i));
					}*/
				}
				return ans;
			}
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	int N;	
	cin >> N   ;
	auto edge = Read<pair<int,int>>(N-1);	
#ifdef _DEBUG		
	//	//printf("N=%d", n);
	//Out(edge, "edge=");
#endif // DEBUG	
		auto res = Solution().Ans(edge);
		cout << res << endl;
	return 0;
}

各种情况都考虑

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 <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;
	scanf("%d", &n);
	vector<T> ret(n);
	for(int i=0;i < n ;i++) {
		cin >> ret[i];
	}
	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;
}
class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, 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, 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<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 Solution {
		public:
			int Ans(vector<pair<int, int>>& edge) {
				const int N = edge.size() + 1;
				int ans = INT_MAX / 2;
				auto Ans = [&](int a, int b, int c) {
					const int iMax = max(max(a, b), c);
					const int iMin = min(min(a, b), c);
					ans = min(ans, iMax - iMin);
				};
				auto neiBo = CNeiBo::Two(N, edge, false, 1);
				auto leves = CBFSLeve::Leve(neiBo, { 0 });
				auto leveNodes = CBFSLeve::LeveNodes(leves);
				auto leveSorts = CBFSLeve::LeveSort(leves);
				vector<int> vs2(leveSorts.rbegin(), leveSorts.rend());
				vector<vector<int>> childs(N);
				for (int i = 0; i < N; i++) {
					for (const auto& next : neiBo[i]) {
						if (leves[next] < leves[i]) { continue; }
						childs[i].emplace_back(next);
					}
				}
				vector<int> cnt(N, 1);
				for (const auto& cur : vs2) {
					for (const auto& chi : childs[cur]) {
						cnt[cur] += cnt[chi];
					}
				}
				for (auto& v : childs) {
					for (int i = 1; i < v.size(); i++) {
						if (cnt[v[i]] > cnt[v[0]]) { swap(v[i], v[0]); }
					}
				}
				vector<set<int>> cc(N);
				auto Do33 = [&N, Ans](set<int>& tmp, int ccnt) {
					const int x = (N - ccnt) / 2;
					auto it = tmp.upper_bound(x);
					if (tmp.end() != it) {
						Ans(ccnt, *it, N - ccnt - *it);
					}
					if (tmp.begin() != it) {
						Ans(ccnt, *prev(it), N - ccnt - *prev(it));
					}
				};
				auto Do = [&](int cur) {
					cc[cur].swap(cc[childs[cur][0]]);
					for (int i = 1; i < childs[cur].size(); i++) {
						const auto& chi = childs[cur][i];
						cc[cur].insert(cc[chi].begin(), cc[chi].end());
					}
					auto it = cc[cur].upper_bound(cnt[cur] / 2);
					if (cc[cur].end() != it) {
						Ans(cnt[cur] - *it, *it, N - cnt[cur]);
					}
					if (cc[cur].begin() != it) {
						Ans(cnt[cur] - *prev(it), *prev(it), N - cnt[cur]);
					}
				};
				vector<vector<int>> tv(leveNodes.rbegin(), leveNodes.rend());
				for (auto& v : tv )
				{
					for (int i = 1; i < v.size(); i++) {
						if (cnt[v[i]] > cnt[v[0]]) { swap(v[0], v[i]); }
					}
					for (const auto& cur : v) {
						if (!childs[cur].empty()) { Do(cur); }
						cc[cur].emplace(cnt[cur]);
					}
					set<int> tmp1;
					for (int i = 1; i < v.size(); i++) {
						Do33(cc[v[0]], cnt[v[i]]);
						Do33(tmp1, cnt[v[i]]);
						tmp1.insert(cc[v[i]].begin(), cc[v[i]].end());
					}
					set<int> tmp2;
					for (int i = v.size() - 1; i > 0; i--) {
						tmp2.insert(cc[v[i]].begin(), cc[v[i]].end());
						Do33(tmp2, cnt[v[i - 1]]);
					}
				}
				return ans;
			}
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	int N;	
	cin >> N   ;
	auto edge = Read<pair<int,int>>(N-1);	
#ifdef _DEBUG		
	//	//printf("N=%d", n);
	//Out(edge, "edge=");
#endif // DEBUG	
		auto res = Solution().Ans(edge);
		cout << res << endl;
	return 0;
}

单元测试

cpp 复制代码
vector<pair<int, int>> edge;
		TEST_METHOD(TestMethod1)
		{
			edge = { {1,2},{1,3} };
			auto res = Solution().Ans(edge);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod11)
		{			
			edge = { {1,2},{2,3},{3,4} };
			auto res = Solution().Ans(edge);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod12)
		{
			edge = { {1,2},{1,3},{3,4},{3,5},{5,6} };
			auto res = Solution().Ans(edge);
			AssertEx(0, res);
		}	
		TEST_METHOD(TestMethod13)
		{
			edge = { {1,3},{2,3},{3,4},{3,5},{5,6},{5,7},{7,8},{7,9} };
			auto res = Solution().Ans(edge);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod14)
		{
			edge = { {1,2},{1,3},{3,4} };
			auto res = Solution().Ans(edge);
			AssertEx(1, 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++**实现。

相关推荐
alexwang2111 小时前
AT_abc458_d [ABC458D] Chalkboard Median题解
c++·算法·题解·atcoder
故事和你911 小时前
洛谷-【图论2-4】连通性问题1
开发语言·数据结构·c++·算法·动态规划·图论
我先去打把游戏先1 小时前
Ubuntu虚拟机(服务器版本)Git安装教程(附常用命令)——从零开始掌握版本控制
服务器·c语言·c++·git·嵌入式硬件·物联网·ubuntu
周末也要写八哥1 小时前
算法实例分析:使数组相等的最小开销
算法
吃好睡好便好1 小时前
在Matlab中绘制质点运动轨迹图
开发语言·学习·算法·matlab·信息可视化
艾莉丝努力练剑1 小时前
【Linux网络】Linux 网络编程:HTTP(四)从手写服务器到生产级 Nginx 与 cpp-httplib 实战
linux·运维·服务器·网络·c++·nginx·http
爱炼丹的James1 小时前
第三章 搜索和图论
数据结构·算法·图论
菜菜笔记1 小时前
【无标题】
算法
咩咦1 小时前
C++学习笔记21:日期类加减天数
c++·学习笔记·运算符重载·日期类·operator+·operator+=