【贪心】P7076 [CSP-S2020] 动物园|普及

本文涉及知识点

C++贪心

[CSP-S2020] 动物园

题目描述

动物园里饲养了很多动物,饲养员小 A 会根据饲养动物的情况,按照《饲养指南》购买不同种类的饲料,并将购买清单发给采购员小 B。

具体而言,动物世界里存在 2 k 2^k 2k 种不同的动物,它们被编号为 0 ∼ 2 k − 1 0 \sim 2^k - 1 02k1。动物园里饲养了其中的 n n n 种,其中第 i i i 种动物的编号为 a i a_i ai

《饲养指南》中共有 m m m 条要求,第 j j j 条要求形如“如果动物园中饲养着某种动物,满足其编号的二进制表示的第 p j p_j pj 位为 1 1 1,则必须购买第 q j q_j qj 种饲料”。其中饲料共有 c c c 种,它们从 1 ∼ c 1 \sim c 1c 编号。本题中我们将动物编号的二进制表示视为一个 k k k 位 01 串,第 0 0 0 位是最低位,第 k − 1 k - 1 k1 位是最高位。

根据《饲养指南》,小 A 将会制定饲料清单交给小 B,由小 B 购买饲料。清单形如一个 c c c 01 01 01 串,第 i i i 位为 1 1 1 时,表示需要购买第 i i i 种饲料;第 i i i 位为 0 0 0 时,表示不需要购买第 i i i 种饲料。 实际上根据购买到的饲料,动物园可能可以饲养更多的动物。更具体地,如果将当前未被饲养的编号为 x x x 的动物加入动物园饲养后,饲料清单没有变化,那么我们认为动物园当前还能饲养编号为 x x x 的动物。

现在小 B 想请你帮忙算算,动物园目前还能饲养多少种动物。

输入格式

第一行包含四个以空格分隔的整数 n , m , c , k n, m, c, k n,m,c,k
分别表示动物园中动物数量、《饲养指南》要求数、饲料种数与动物编号的二进制表示位数。
第二行 n n n 个以空格分隔的整数,其中第 i i i 个整数表示 a i a_i ai
接下来 m m m 行,每行两个整数 p i , q i p_i, q_i pi,qi 表示一条要求。
数据保证所有 a i a_i ai 互不相同,所有的 q i q_i qi 互不相同。

输出格式

仅一行一个整数表示答案。

样例 #1

样例输入 #1

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

样例输出 #1

13

样例 #2

样例输入 #2

2 2 4 3
1 2
1 3
2 4

样例输出 #2

2

样例 #3

样例输入 #3

见附件中的 zoo/zoo3.in

样例输出 #3

见附件中的 zoo/zoo3.ans

提示

【样例 #1 解释】

动物园里饲养了编号为 1 , 4 , 6 1, 4, 6 1,4,6 的三种动物,《饲养指南》上的三条要求为:

  1. 若饲养的某种动物的编号的第 0 0 0 个二进制位为 1 1 1,则需购买第 3 3 3 种饲料。
  2. 若饲养的某种动物的编号的第 2 2 2 个二进制位为 1 1 1,则需购买第 4 4 4 种饲料。
  3. 若饲养的某种动物的编号的第 2 2 2 个二进制位为 1 1 1,则需购买第 5 5 5 种饲料。

饲料购买情况为:

  1. 编号为 1 1 1 的动物的第 0 0 0 个二进制位为 1 1 1,因此需要购买第 3 3 3 种饲料;
  2. 编号为 4 , 6 4, 6 4,6 的动物的第 2 2 2 个二进制位为 1 1 1,因此需要购买第 4 , 5 4, 5 4,5 种饲料。

由于在当前动物园中加入一种编号为 0 , 2 , 3 , 5 , 7 , 8 , … , 15 0, 2, 3, 5, 7, 8, \ldots , 15 0,2,3,5,7,8,,15 之一的动物,购物清单都不会改变,因此答案为 13 13 13

【数据范围】

对于 20 % 20 \% 20% 的数据, k ≤ n ≤ 5 k \le n \le 5 kn5 m ≤ 10 m \le 10 m10 c ≤ 10 c \le 10 c10,所有的 p i p_i pi 互不相同。
对于 40 % 40 \% 40% 的数据, n ≤ 15 n \le 15 n15 k ≤ 20 k \le 20 k20 m ≤ 20 m \le 20 m20 c ≤ 20 c \le 20 c20
对于 60 % 60 \% 60% 的数据, n ≤ 30 n \le 30 n30 k ≤ 30 k \le 30 k30 m ≤ 1000 m \le 1000 m1000
对于 100 % 100 \% 100% 的数据, 0 ≤ n , m ≤ 1 0 6 0 \le n, m \le 10^6 0n,m106 0 ≤ k ≤ 64 0 \le k \le 64 0k64 1 ≤ c ≤ 1 0 8 1 \le c \le 10^8 1c108

贪心

2z-n。
z= 0
如果拥有pj的动物,则需要购买他所需要的所有饲料。vector<vector> need(k);记录动物需要的饲料。unordered_set has;记录需要购买的饲料
枚举各位,如果此动物需要的饲料都在has中存在,z++。

1ull << 64 会溢出。 所以:
如果 已有动物为0,z= 64。新增的动物数量会超出 uint64。故直接用字符串表示。
如果已有动物不为0,z = 64,则:

return ULLONG_MAX - (unsigned long long)a.size() + 1ull;

** 注意**:
一,不同的动物可能需要相同的饲料。
二,一种动物可能需要多种饲料。

代码

核心代码

#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 <bitset>
using namespace std;



template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {
	vector<T> ret;
	T d ;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

template<class T = int>
vector<T> Read( const char* pFormat = "%d") {
	int n;
	scanf("%d", &n);
	vector<T> ret;
	T d;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

string ReadChar(int n) {
	string str;
	char ch;
	while (n--) {
		do
		{
			scanf("%c", &ch);
		} while (('\n' == ch));
			str += ch;
	}
	return str;
}

class Solution {
public:
	unsigned long long MaxS(bool& bErr, const vector<unsigned long long>& a, const vector<pair<int, int>>& buy, int C, int k) {
		bErr = false;
		vector<bool> mask(k);//记录已有动物
		for (const auto& i : a) {
			for (int j = 0; j < k; j++) {
				if ((1ULL << j) & i) { mask[j] = true; }
			}
		}
		unordered_set<int> has;//记录需要购买的饲料
		vector<vector<int>> need(k);//动物需要的饲料
		for (const auto& [i, tmp] : buy) {
			need[i].emplace_back(tmp);
			if (mask[i]) { has.emplace(tmp); }
		}

		int z = 0;
		for (int i = 0; i < k; i++) {
			bool can = true;
			for (const auto& j : need[i]) {
				if (!has.count(j)) { can = false; }//缺少此动物需要的饲料
			}
			z += can;
		}
		if (64 == z) {
			if (a.size() == 0) { bErr = true; }
			return ULLONG_MAX - (unsigned long long)a.size() + 1ull;
		}
		return (1ULL << z) - (unsigned long long)a.size();
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int n,m,c,k;		
	scanf("%d%d%d%d", &n,&m,&c,&k);
	auto a = Read<unsigned long long>(n,"%llu");
	vector<pair<int, int>> buy;
	for (int i = 0; i < m; i++) {
		int i1, i2;
		scanf("%d%d", &i1, &i2);
		buy.emplace_back(i1, i2);
	}	
	bool bErr;
	auto res = Solution().MaxS(bErr,a,buy,c,k);
	if (bErr) {
		puts("18446744073709551616"); return 0;
	}
	cout << res << std::endl;
	return 0;
}

单元测试

vector<unsigned long long> a;
		vector<pair<int, int>> buy;
		bool bErr;
		TEST_METHOD(TestMethod11)
		{
			a = { 1 ,4 ,6 }, buy = { {0,3},{2,4},{2,5} };
			auto res = Solution().MaxS(bErr,a, buy,5,4 );
			AssertEx(13ULL, res);
		}
		TEST_METHOD(TestMethod12)
		{
			a = { 1 ,2 }, buy = { {1,3},{2,4} };
			auto res = Solution().MaxS(bErr,a, buy,4, 3);
			AssertEx(2ULL, res);
		}
		TEST_METHOD(TestMethod13)
		{
			a = { ULLONG_MAX }, buy = {};
			auto res = Solution().MaxS(bErr,a, buy, 1,64);
			AssertEx(ULLONG_MAX, res);
		}
		TEST_METHOD(TestMethod14)
		{
			a = {  }, buy = {};
			auto res = Solution().MaxS(bErr, a, buy,1, 64);
			AssertEx(true, bErr);
		}
		TEST_METHOD(TestMethod15)
		{
			a = {  }, buy = { {1,1} };
			auto res = Solution().MaxS(bErr, a, buy,1, 64);
			AssertEx(false, bErr);
		}
		TEST_METHOD(TestMethod16)
		{
			a = { 1 }, buy = {  };
			auto res = Solution().MaxS(bErr, a, buy,1, 64);
			AssertEx(false, bErr);
		}
		TEST_METHOD(TestMethod17)
		{
			a = { 1 }, buy = { {0,1},{1,1} };
			auto res = Solution().MaxS(bErr, a, buy, 1, 2);
			AssertEx(3ull, res);
		}
		TEST_METHOD(TestMethod18)
		{
			a = { 1 }, buy = { {1,1},{0,1},{0,2} };
			auto res = Solution().MaxS(bErr, a, buy, 2, 2);
			AssertEx(3ull, res);
		}
		TEST_METHOD(TestMethod19)
		{
			a = { 2 }, buy = { {1,1},{0,1},{0,2} };
			auto res = Solution().MaxS(bErr, a, buy, 2, 2);
			AssertEx(1ull, 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++**实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值