本文涉及的基础知识点

C++图论
C++DFS

[GESP202406 七级] 黑白翻转

题目描述

小杨有一棵包含 n n n 个节点的树,这棵树上的任意一个节点要么是白色,要么是黑色。小杨认为一棵树是美丽树当且仅当在删除所有白色节点之后,剩余节点仍然组成一棵树。

小杨每次操作可以选择一个白色节点将它的颜色变为黑色,他想知道自己最少要执行多少次操作可以使得这棵树变为美丽树。

输入格式

第一行包含一个正整数 n n n,代表树的节点数。

第二行包含 n n n 个非负整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an,其中如果 a i = 0 a_i=0 ai=0,则节点 i i i 的颜色为白色,否则为黑色。

之后 n − 1 n-1 n1 行,每行包含两个正整数 x i , y i x_i,y_i xi,yi,代表存在一条连接节点 x i x_i xi y i y_i yi 的边。

输出格式

输出一个整数,代表最少执行的操作次数。

样例 #1

样例输入 #1

5
0 1 0 1 0
1 2
1 3
3 4
3 5

样例输出 #1

2

提示

样例解释

将节点 1 1 1 3 3 3 变为黑色即可使这棵树变为美丽树,此时删除白色节点 5 5 5,剩余黑色节点仍然组成一棵树。

数据范围

子任务编号数据点占比 n n n a i a_i ai特殊条件
1 1 1 30 % 30\% 30% ≤ 10 5 \leq 10^5 105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0ai1树的形态为一条链
2 2 2 30 % 30\% 30% ≤ 10 5 \leq 10^5 105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0ai1只有两个节点颜色为黑色
3 3 3 40 % 40\% 40% ≤ 10 5 \leq 10^5 105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0ai1

对于全部数据,保证有 1 ≤ n ≤ 10 5 1\leq n\leq 10^5 1n105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0ai1

错误解法:图论 树

转成以0为根的有根树。cnt[i]记录i子树,黑色节点的数量,如果cnt[i] >0且i是白色节点,ans++。
本解:
假定删除一个节点后,会分成多个子树,变色。
错误原因:
根子树可能为空,如:cur是0。
也可能根子树全部是变色,会被删除。

拓扑排序

白色叶子节点,才会删除。白色节点的数量-删除的叶子的数量就是答案。

代码

#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<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<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<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 CTopSort
{
public:
	template <class T = vector<int> >
	void Init(const vector<T>& vNeiBo, bool bDirect)
	{
		const int iDelOutDeg = bDirect ? 0 : 1;
		m_c = vNeiBo.size();
		m_vBackNeiBo.resize(m_c);
		vector<int> vOutDeg(m_c);
		for (int cur = 0; cur < m_c; cur++)
		{
			vOutDeg[cur] = vNeiBo[cur].size();
			for (const auto& next : vNeiBo[cur])
			{
				m_vBackNeiBo[next].emplace_back(cur);
			}
		}
		vector<bool> m_vHasDo(m_c);
		queue<int> que;
		for (int i = 0; i < m_c; i++)
		{
			if (vOutDeg[i] <= iDelOutDeg)
			{
				m_vHasDo[i] = true;
				if (OnDo(i)) {
					que.emplace(i);
				}
			}
		}

		while (que.size())
		{
			const int cur = que.front();
			que.pop();
			for (const auto& next : m_vBackNeiBo[cur])
			{
				if (m_vHasDo[next])
				{
					continue;
				}
				vOutDeg[next]--;
				if (vOutDeg[next] <= iDelOutDeg)
				{
					m_vHasDo[next] = true;
					if (OnDo(next)) {
						que.emplace(next);
					}
				}
			}
		};
	}
	int m_c;
protected:
	virtual bool OnDo(int cur) = 0;
	vector<vector<int>> m_vBackNeiBo;
};

class CMyTTopSort :public CTopSort {
public:
	CMyTTopSort(const vector<int>& a) :m_a(a) {

	}
	virtual bool OnDo(int cur)
	{
		if (0 == m_a[cur]) { m_iDel++; return true; }
		return false;
	}
	const vector<int>& m_a;
	int m_iDel = 0;
};
//P10723 [GESP202406 七级] 黑白翻转
class Solution {
public:
	int Ans(const vector<int>& a, vector<pair<int, int>>& edge) {
		const int N = a.size();
		auto neiBo = CNeiBo::Two(N, edge, false, 1);
		CMyTTopSort top(a);
		top.Init(neiBo, false);
		const int c1 = count(a.begin(), a.end(), 0);
		return c1 - top.m_iDel;
	}
};


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

单元测试

vector<int> a;
		vector<pair<int, int>> edge;
		TEST_METHOD(TestMethod11)
		{
			a = { 0,1,0,1,0 }, edge = { {1,2},{1,3},{3,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod12)
		{
			a = { 0,0,0,0,0 }, edge = { {1,2},{2,3},{3,4},{4,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod13)
		{
			a = { 1,1,1,1,0 }, edge = { {1,2},{2,3},{3,4},{4,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod14)
		{
			a = { 1,1,1,0,1 }, edge = { {1,2},{2,3},{3,4},{4,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod15)
		{
			a = { 1,1,1,0,1 }, edge = { {1,2},{1,3},{2,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod16)
		{
			a = { 1,1,0,1,1 }, edge = { {1,2},{1,3},{2,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod17)
		{
			a = { 1,1,1,0,0 }, edge = { {1,2},{1,3},{2,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod18)
		{
			a = { 0,1,1,1,1 }, edge = { {1,2},{1,3},{2,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod19)
		{
			a = { 0,0,0,1,1 }, edge = { {1,2},{1,3},{2,4},{3,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(3, res);
		}
		TEST_METHOD(TestMethod20)
		{
			const int N = 9;
			a.resize(N);
			edge.resize(N - 1);
			for (int i = 0; i + 1 < N; i++) {
				edge[i]=make_pair(i + 1, i + 2);
			}
			for (int i = 0; i < (1<<N); i++) {
				for (int j = 0; j <N; j++) {
					a[j] = i && (1 << j);
				}
				auto res = Solution().Ans(a, edge);
				int res1 = count(a.begin(), a.end(), 0);
				int c1 = 0,c2=0;
				while ((c1 < N) && (a[c1] == 0)) {
					c1++;
				}
				while ((c2 <N) && (a[N - 1 - c2] == 0)) {
					c2++;
				}
				res1 = max(0, res1 - c1 - c2);
				AssertEx(res1, res);
			}
		}
		TEST_METHOD(TestMethod21)
		{
			a = { 0,1,1,1,1 }, edge = { {1,2},{2,3},{3,4},{4,5} };
			auto res = Solution().Ans(a, edge);
			AssertEx(0, res);
		}

扩展阅读

计算几何为骨,排样优化为魂
作品:亲士CAD工具箱
经典文章推荐:二维排样
万物皆数学
查阅鄙人的博文,请点击博文下载学院导航
活到老,学到老。明朝中后期,大约50%的进士能当上堂官(副部及更高);能当上堂官的举人只有十余人。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐