Forming New Sets

Just as numbers can be added, subtracted, and multiplied, we can manipulate sets in certain basic ways. The natural operations on sets are to combine their elements, to find those elements common to both sets, and to determine which elements belong to one set but not another.
Just as graph theory is the mathematical study of graphs and their properties, set theory is the mathematical study of sets and their properties.

Problem

If A and B are sets, then their union A∪B is the set comprising any elements in either A or B; their intersection A∩B is the set of elements in both A and B; and their set difference A−B is the set of elements in A but not in B.
Furthermore, if A is a subset of another set U, then the set complement of A with respect to U is defined as the set Ac=U−A. See the Sample sections below for examples.
Given: A positive integer n (n≤20,000) and two subsets A and B of {1,2,…,n}.
Return: Six sets: A∪B, A∩B, A−B, B−A, Ac, and Bc (where set complements are taken with respect to {1,2,…,n}

Sample Dataset

10
{1, 2, 3, 4, 5}
{2, 8, 5, 10}

Sample Output

{1, 2, 3, 4, 5, 8, 10}
{2, 5}
{1, 3, 4}
{8, 10}
{8, 9, 10, 6, 7}
{1, 3, 4, 6, 7, 9}

Extra Information

From the definitions above, one can see that A∪B=B∪A and A∩B=B∩A for all sets A and B, but it is not necessarily the case that A−B=B−A (as seen in the Sample sections above). This set theoretical fact parallels the arithmetical fact that addition is commutative but subtraction is not.

Solution

本例不打算使用内置数据结构,因为这样解题没有意思。同时也不打算实现性能好的平衡树,只使用排序数组进行模拟。
算法流程:

  1. 定义 有序 全集 Introduction to Set Operations - 图2,对输入两个集合放入数组后排序。
  2. 执行相关集合操作。

由于两个集合都是有序的,因此可以加快查找,所以在下面操作中将会使用 双指针 策略,用 Introduction to Set Operations - 图3 索引指向 Introduction to Set Operations - 图4 中元素即 Introduction to Set Operations - 图5;用 Introduction to Set Operations - 图6 索引指向 Introduction to Set Operations - 图7 中元素即 Introduction to Set Operations - 图8

(1)并集:Introduction to Set Operations - 图9

  1. Introduction to Set Operations - 图10,此时只需要添加任一一个数字即可。同时 ++i, ++j
  2. Introduction to Set Operations - 图11
    • 因为 Introduction to Set Operations - 图12,即之后不可能重复出现 Introduction to Set Operations - 图13,添加 Introduction to Set Operations - 图14,同时 ++i
    • 为避免重复 j 指针不动 ,因为 Introduction to Set Operations - 图15 可能有元素与 Introduction to Set Operations - 图16 相等
  3. Introduction to Set Operations - 图17
    • 因为 Introduction to Set Operations - 图18,即之后不可能重复出现 Introduction to Set Operations - 图19,添加 Introduction to Set Operations - 图20,同时 ++j
    • 为避免重复 i 指针不动,因为 Introduction to Set Operations - 图21 可能有元素与 Introduction to Set Operations - 图22 相等 ``` i = 0, j = 0: A[i] < B[j] 添加 A[i], 则 A∪B = {1}; ++i A: 1 2 8 i B: 2 5 8 10 j

i = 1, j = 0: A[i] = B[j] 添加 A[i]/B[j], 则 A∪B = {1, 2}; ++i, ++j A: 1 2 8 i B: 2 5 8 10 j

i = 2, j = 1: A[i] > B[j] 添加 B[j], 则 A∪B = {1, 2, 5}; ++j A: 1 2 8 i B: 2 5 8 10 j

i = 2, j = 2: A[i] = B[j] 添加 A[i]/B[j], 则 A∪B = {1, 2, 5, 8}; ++i,++j A: 1 2 8 i B: 2 5 8 10 j

i = 3, j = 3: i 超界,添加剩余 B[j+1,n) ,则 A∪B = {1, 2, 5, 8, 10} A: 1 2 8 i B: 2 5 8 10 j

  1. <a name="DyXN0"></a>
  2. ### (2)交集:![](https://cdn.nlark.com/yuque/__latex/36644e97756f76b26077c7c828edc492.svg#card=math&code=A%20%5Ccap%20B&height=13&width=39)
  3. 1. 当 ![](https://cdn.nlark.com/yuque/__latex/d177425e9f57f6a4d84adaa84cfc8cfd.svg#card=math&code=A%5Bi%5D%20%3D%20B%5Bj%5D&height=18&width=70),此时只需要添加任一**一个数字**即可。同时 `++i, ++j`
  4. 1. 若 ![](https://cdn.nlark.com/yuque/__latex/ec11c447591fd05d4083d334086a3b0d.svg#card=math&code=A%5Bi%5D%20%5Clt%20B%5Bj%5D&height=18&width=70):
  5. - 因为 ![](https://cdn.nlark.com/yuque/__latex/c05eee81039d502e5b8d9d4031fcf749.svg#card=math&code=A%5Bi%2B1%2Cn%29%20%5Cgt%20A%5Bi%5D%2C%20B%5Bj%2Cn%29%20%5Cgt%20A%5Bi%5D&height=18&width=206),即之后不可能重复出现 ![](https://cdn.nlark.com/yuque/__latex/8a6b5ab46e06fa60418f7c34e624b076.svg#card=math&code=A%5Bi%5D&height=18&width=24),因此 `++i`
  6. - 为避免遗漏 `j` 指针不动 ,因为 ![](https://cdn.nlark.com/yuque/__latex/a7f8e70516908cdfc0dc18b4b2bb634d.svg#card=math&code=A%5Bi%2B1%2C%20n%29&height=18&width=67) 可能有元素与 ![](https://cdn.nlark.com/yuque/__latex/d4f2dcfc8b7ed9feda82a157b4949554.svg#card=math&code=B%5Bj%5D&height=18&width=25) 相等
  7. - 若 ![](https://cdn.nlark.com/yuque/__latex/d98ead45039462b8e46cdc7631f1096f.svg#card=math&code=A%5Bi%5D%20%5Cgt%20B%5Bj%5D&height=18&width=70):
  8. - 因为 ![](https://cdn.nlark.com/yuque/__latex/bfbfa7580a649712d96820a2926db686.svg#card=math&code=B%5Bj%2B1%2Cn%29%20%5Cgt%20B%5Bj%5D%2C%20A%5Bi%2Cn%29%20%5Cgt%20B%5Bj%5D&height=18&width=209),即之后不可能重复出现 ![](https://cdn.nlark.com/yuque/__latex/d4f2dcfc8b7ed9feda82a157b4949554.svg#card=math&code=B%5Bj%5D&height=18&width=25),因此 `++j`
  9. - 为避免遗漏 `i` 指针不动,因为 ![](https://cdn.nlark.com/yuque/__latex/30b250f6e287b31abe0f71011baab3cc.svg#card=math&code=B%5Bj%2B1%2C%20n%29&height=18&width=69) 可能有元素与 ![](https://cdn.nlark.com/yuque/__latex/8a6b5ab46e06fa60418f7c34e624b076.svg#card=math&code=A%5Bi%5D&height=18&width=24) 相等

i = 0, j = 0: A[i] < B[j], 则 ++i; A: 1 2 3 4 5 i B: 2 5 8 10 j

i = 1, j = 0: A[i] = B[j] 添加 A[i]/B[j], A∩B = {2}; ++i, ++j A: 1 2 3 4 5 i B: 2 5 8 10 j

i = 2, j = 1: A[i] < B[j], 则 ++i; A: 1 2 3 4 5 i B: 2 5 8 10 j

i = 3, j = 1: A[i] < B[j], 则 ++i; A: 1 2 3 4 5 i B: 2 5 8 10 j

i = 4, j = 1: A[i] = B[j] 添加 A[i]/B[j], A∩B = {2, 5}; ++i, ++j A: 1 2 3 4 5 i B: 2 5 8 10 j

i = 5, j = 2: i 超界,不可能再有交集 A: 1 2 3 4 5 i B: 2 5 8 10 j

  1. <a name="UaTeU"></a>
  2. ### (3)补集:![](https://cdn.nlark.com/yuque/__latex/0fab86746f2c4a489ae8d5d8c7d2e311.svg#card=math&code=A%5E%7BC%7D%2C%20B%5E%7BC%7D&height=19&width=48)
  3. 假设两个集合都是有序的且有 ![](https://cdn.nlark.com/yuque/__latex/2d11d86f896557c1cf36f311bb392f20.svg#card=math&code=A%20%5Csubsete%27q%20C&height=15&width=42),因此我们可以使用双指针直接 `pick` 非 ![](https://cdn.nlark.com/yuque/__latex/0c41c78a817aa659629bcf6f8782858f.svg#card=math&code=A%5Bj%5D&height=18&width=25) 元素即可,遍历 ![](https://cdn.nlark.com/yuque/__latex/33e2ae800eceebbed42f2c3e417eb2fc.svg#card=math&code=C%5Bi%5D&height=18&width=24) 元素:
  4. 1. 若 ![](https://cdn.nlark.com/yuque/__latex/a1a62a24a94f5f24f998769f2ed580c2.svg#card=math&code=C%5Bi%5D%20%3D%20A%5Bj%5D&height=18&width=70) 则不将当前 ![](https://cdn.nlark.com/yuque/__latex/33e2ae800eceebbed42f2c3e417eb2fc.svg#card=math&code=C%5Bi%5D&height=18&width=24) 记录,![](https://cdn.nlark.com/yuque/__latex/4ac321dc5c22a59e652e75720ba2f066.svg#card=math&code=%2B%2Bi%2C%20%2B%2Bj&height=16&width=78)
  5. 1. 若 ![](https://cdn.nlark.com/yuque/__latex/0877ddae61a65ff3d3d36e3377c32c30.svg#card=math&code=C%5Bi%5D%20%5Clt%20A%5Bj%5D&height=18&width=70):
  6. - 因为 ![](https://cdn.nlark.com/yuque/__latex/ad64ce0aa3ada4308a0aeb5ad575529c.svg#card=math&code=C%5Bi%2B1%2Cn%29%20%5Cgt%20C%5Bi%5D%2C%20A%5Bj%2Cn%29%20%5Cgt%20C%5Bi%20%5D&height=18&width=207),即之后不可能重复出现 ![](https://cdn.nlark.com/yuque/__latex/33e2ae800eceebbed42f2c3e417eb2fc.svg#card=math&code=C%5Bi%5D&height=18&width=24),因此添加 ![](https://cdn.nlark.com/yuque/__latex/33e2ae800eceebbed42f2c3e417eb2fc.svg#card=math&code=C%5Bi%5D&height=18&width=24) 元素,同时 `++i`
  7. - 为避免重复 `j` 指针不动 ,因为 ![](https://cdn.nlark.com/yuque/__latex/3a3ffa384068321f9e2d0df7b5002245.svg#card=math&code=C%5Bi%2B1%2C%20n%29&height=18&width=67) 可能有元素与 ![](https://cdn.nlark.com/yuque/__latex/0c41c78a817aa659629bcf6f8782858f.svg#card=math&code=A%5Bj%5D&height=18&width=25) 相等
  8. 3. 若 ![](https://cdn.nlark.com/yuque/__latex/345663d37cd71cecaa1bcbd8c197d294.svg#card=math&code=C%5Bi%5D%20%5Cgt%20A%5Bj%5D&height=18&width=70):
  9. - 因为 ![](https://cdn.nlark.com/yuque/__latex/951ae926fa6856492eacbc27da3237bd.svg#card=math&code=A%5Bj%2B1%2Cn%29%20%5Cgt%20A%5Bj%5D%2C%20C%5Bi%2Cn%29%20%5Cgt%20A%5Bj%5D&height=18&width=209),即之后不可能重复出现 ![](https://cdn.nlark.com/yuque/__latex/0c41c78a817aa659629bcf6f8782858f.svg#card=math&code=A%5Bj%5D&height=18&width=25),同时 `++j`
  10. - 为避免重复 `i` 指针不动 ,因为 ![](https://cdn.nlark.com/yuque/__latex/7375889ea2d674142dd58eb9c0c7fd9c.svg#card=math&code=A%5Bj%2B1%2C%20n%29&height=18&width=68) 可能有元素与 ![](https://cdn.nlark.com/yuque/__latex/33e2ae800eceebbed42f2c3e417eb2fc.svg#card=math&code=C%5Bi%5D&height=18&width=24) 相等

i = 0, j = 0: C[i] = A[j], 则 ++i, ++j; C: 1 2 4 5 6 7 8 i A: 1 3 4 j

i = 0, j = 0: C[i] < A[j], 添加 C[i], A ^ C = {2} ;++i, ++j; C: 1 2 4 5 6 7 8 i A: 1 3
j

i = 2, j = 1: C[i] > A[j],可能 A[j+1,n) 存在 C[i]; ++j; C: 1 2 4 5 6 7 8 i A: 1 3 4 j

  1. <a name="JtzFU"></a>
  2. ### (4)差集:![](https://cdn.nlark.com/yuque/__latex/6db6619e95231cadaabc84c4cafee212.svg#card=math&code=A-B%2C%20B%20-%20A&height=16&width=88)
  3. 根据上面所求交集,我们就可以得到 ![](https://cdn.nlark.com/yuque/__latex/9a0945636b45b676c067ba04aee8568e.svg#card=math&code=A%20-%20B%20%3D%20A%5E%7BA%20%5Ccap%20B%7D%2C%20B%20-%20A%20%3D%20B%5E%7BA%20%5Ccap%20B%7D&height=19&width=199),也就是它们各自去除交集的补集。直接调用上面补集函数即可。
  4. ```cpp
  5. #include <iostream>
  6. #include <vector>
  7. #include <algorithm>
  8. using namespace std;
  9. class Set {
  10. public:
  11. Set() {};
  12. Set(vector<int> &nums) {
  13. // 假设输入 nums 不包含重复值
  14. for (auto num: nums) add(num);
  15. sort(sortNums.begin(), sortNums.end()); // 排序
  16. };
  17. void add(int val) {
  18. sortNums.push_back(val);
  19. }
  20. void printSet() {
  21. cout << "{";
  22. int n = sortNums.size();
  23. for (int i = 0; i < n - 1; ++i) cout << sortNums[i] << ", ";
  24. if (n > 0) cout << sortNums[n-1];
  25. cout << "}" << endl;
  26. }
  27. Set unite(Set &t) {
  28. int m = this->sortNums.size(), i = 0;
  29. int n = t.sortNums.size(), j = 0;
  30. Set res;
  31. while (i < m && j < n) {
  32. if (this->sortNums[i] == t.sortNums[j]) {
  33. res.add(this->sortNums[i]);
  34. ++i, ++j;
  35. } else if (this->sortNums[i] < t.sortNums[j]) {
  36. res.add(this->sortNums[i]);
  37. ++i;
  38. } else {
  39. res.add(t.sortNums[j]);
  40. ++j;
  41. }
  42. }
  43. while (i < m) res.add(this->sortNums[i++]);
  44. while (j < n) res.add(t.sortNums[j++]);
  45. return res;
  46. }
  47. Set intersection(Set &t) {
  48. int m = this->sortNums.size(), i = 0;
  49. int n = t.sortNums.size(), j = 0;
  50. Set res;
  51. while (i < m && j < n) {
  52. if (this->sortNums[i] == t.sortNums[j]) {
  53. res.add(this->sortNums[i]);
  54. ++i, ++j;
  55. } else if (this->sortNums[i] < t.sortNums[j]) {
  56. ++i;
  57. } else {
  58. ++j;
  59. }
  60. }
  61. return res;
  62. }
  63. Set difference(Set &t) {
  64. // s - t
  65. int m = this->sortNums.size(), i = 0;
  66. int n = t.sortNums.size(), j = 0;
  67. Set res;
  68. while (i < m && j < n) {
  69. if (this->sortNums[i] == t.sortNums[j]) {
  70. ++i, ++j;
  71. } else if (this->sortNums[i] < t.sortNums[j]) {
  72. res.add(this->sortNums[i]);
  73. ++i;
  74. } else {
  75. ++j;
  76. }
  77. }
  78. while (i < m) res.add(this->sortNums[i++]); // t 中没有全部加入
  79. return res;
  80. }
  81. Set compliment(Set &c) {
  82. // c 是全集
  83. return c.difference(*this);
  84. }
  85. private:
  86. vector<int> sortNums;
  87. };
  88. vector<int> extractNumber(string &s) {
  89. int i = 0, n = s.size();
  90. vector<int> ret;
  91. while (i < n) {
  92. while (i < n && !isdigit(s[i])) ++i; // 跳过非数字字符
  93. if (i >= n) break;
  94. int num = 0;
  95. while (i < n && isdigit(s[i])) num = num * 10 + (s[i++] - '0');
  96. ret.push_back(num);
  97. }
  98. return ret;
  99. }
  100. int main() {
  101. int n;
  102. cin >> n;
  103. getchar(); // \n
  104. string tmp;
  105. // getline 读入一行
  106. getline(cin, tmp, '\n');
  107. vector<int> ret = extractNumber(tmp);
  108. Set A(ret);
  109. getline(cin, tmp, '\n');
  110. ret = extractNumber(tmp);
  111. Set B(ret);
  112. vector<int> c(n);
  113. for (int i = 0; i < n; ++i) c[i] = i + 1;
  114. Set C(c);
  115. // A∪B
  116. A.unite(B).printSet();
  117. // A∩B
  118. A.intersection(B).printSet();
  119. // A-B, B-A
  120. A.difference(B).printSet();
  121. B.difference(A).printSet();
  122. // A^{C}, B^{C}
  123. A.compliment(C).printSet();
  124. B.compliment(C).printSet();
  125. return 0;
  126. }