整数分组


整数分组

给定一个包含 $n$ 个整数 $s_1,s_2, /dots ,s_n$ 的集合 $S$。

我们规定,如果某个整数在一个集合中恰好出现一次,则称该整数为超级数。

例如,集合 $/{1,1,2,3,3,3,4/}$ 中包含两个超级数 $2,4$。

现在,请你将 $S$ 分成两个集合 $A$ 和 $B$(其中一个可以为空),使得集合 $A$ 中超级数的数量与集合 $B$ 中超级数的数量相同(也就是说,集合 $A$ 中恰好出现一次的整数的数量与集合 $B$ 中恰好出现一次的整数的数量相同)。

输入格式

第一行包含一个整数 $n$。

第二行包含 $n$ 个整数 $s_1,s_2, /dots ,s_n$。

输出格式

如果不存在合理的划分方案,则输出一行 $NO$。

否则,第一行输出 $YES$,第二行输出一个长度为 $n$ 的由 $A$ 和 $B$ 构成的字符串,其中的第 $i$ 个字符,如果为 $A$ 则表示 $s_i$ 划分到集合 $A$,如果为 $B$ 则表示 $s_i$ 划分到集合 $B$。

如果方案不唯一,输出任意合理方案均可。

数据范围

前 $6$ 个测试点满足 $2 /leq n /leq 12$。
所有测试点满足 $2 /leq n /leq 100$,$1 /leq s_i /leq 100$。

输入样例1:

4
3 5 7 1

输出样例1:

YES
BABA

输入样例2:

3
3 5 1

输出样例2:

NO

 

解题思路

  这题还是比较容易想到的,不过比赛的时候比较紧张,代码写得又长又臭。这一题的细节还是比较多的。

  先扫描一遍序列,统计只出现过一次的数的个数,假设是$k$,分情况讨论:

  1. $2 /mid k$,那么两个集合每个都分$/frac{k}{2}$个就可以了,剩余数的出现次数都大于$1$,可以将他们全部放到$A$或$B$中。最后就可以实现两个集合的超级数的数量都是$/frac{k}{2}$。
  2. $2 /nmid k$,那么一个集合分$/left/lfloor /frac{k}{2} /right/rfloor$个,另外一个集合分$/left/lfloor /frac{k}{2} /right/rfloor + 1$个。然后看一下剩余的数,如果某个数$x$出现了$2$次,那么无论怎么放$x$,都不能使得某个集合的超级数个数增加$1$个(全部$x$放到某个集合或两个集合各放一个$x$),因此如果某个数出现了$2$次那么这个数没有意义。如果某个数$x$出现超过$2$次,那么把一个$x$放到某个集合中,剩余的$x$都放到另外一个集合中,那么就可以使得只放一个$x$的那个集合的超级数个数加$1$。因此如果$k$是奇数,分完了超级数后某个集合的超级数个数少了一个,此时如果存在某个数的出现次数大于$2$,那么都可以构造一个合法方案,而如果剩余数的出现次数都是$2$的话则无解。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110;
 5 
 6 vector<int> p[N];
 7 
 8 int main() {
 9     int n;
10     cin >> n;
11     for (int i = 0; i < n; i++) {
12         int x;
13         cin >> x;
14         p[x].push_back(i);
15     }
16     
17     int cnt = 0;
18     for (int i = 0; i < N; i++) {
19         if (p[i].size() == 1) cnt++;    // 统计出现次数为1的数的个数
20     }
21     
22     string ret(n, 'A'); // 初始化每个数都在集合A中
23     for (int i = 0, k = 0; i < N; i++) {
24         if (p[i].size() == 1 && k < cnt >> 1) { // 前k/2个数放到集合B中,如果cnt是奇数那么最后集合B的超级数比A少1
25             ret[p[i][0]] = 'B';
26             k++;
27         }
28     }
29     
30     bool flag = true;
31     if (cnt & 1) {  // 超级数的个数为奇数
32         flag = false;
33         for (int i = 0; i < N; i++) {
34             if (p[i].size() > 2) {  // 找到某个数的出现次数大于2
35                 ret[p[i][0]] = 'B'; // 取第一个数放到集合B中,剩余的都放到集合A
36                 flag = true;
37                 break;
38             }
39         }
40     }
41     
42     if (flag) cout << "YES/n" << ret;
43     else cout << "NO";
44     
45     return 0;
46 }

 

参考资料

  AcWing 4608. 整数分组(AcWing杯 – 周赛):https://www.acwing.com/video/4274/

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/282632.html

(0)
上一篇 2022年8月28日
下一篇 2022年8月28日

相关推荐

发表回复

登录后才能评论