CF45E - Director(构造性算法 + 字符串 + 贪心 / *2000)
45E - Director(源地址自⇔CF45E)
tag
⇔构造性算法、⇔字符串、⇔贪心、⇔*2000
题意
给定 \(N\) 个姓和 \(N\) 个名,要求两两组合所有的姓和名,使得姓和名开头字母相同的对数最多,且全部姓名的总字典序尽可能的小。
思路
赛时思路(是错的)
点击查看原因
其实这道题的题意非常难懂,在初见的时候以为这是一道打卡水题,所以想的很简单,以为只需要按照字典序进行一遍排序即可。结果错误了两次之后发现并不是如此(然而不信邪的队友便又多交了两发WA......),题目中最关键的那一行条件其实在初见的时候没有读出来,原文如下:
First of all Vasya wants to maximize the number of the pairs, in which the name and the surname start from one letter.
需要先将所有首字母相同的姓和名配对,然后再思考字典序的问题。
可是,这道题的难点还不止于此,思考以下样例:
姓:Sa Ss Aa,名:Sc Sd Sz
如果不考虑字典序,只顾首字母匹配的话,显然,答案为 \(\tt{}Sa\ Sc,\ Ss\ Sd\) 和 \(\tt{}Aa\ Sz\) ,然而,为了让总字典序最小,最佳的匹配应当为 \(\tt{}Sa\ Sd,\ Ss\ Sz\) 和 \(\tt{}Aa\ Sc\) 。
正解(自己做法)
我们按字典序逆序 ( \(\tt{}Z - A\) )考虑本题。
-
假如 \(\tt{}name\) 的数量多于 \(\tt{}surname\) ,并且待匹配数组 \(S\) 中有内容,那么优先将多余的 \(\tt{}name\) 和 \(S\) 匹配(注意, \(\tt{}name\) 中字典序升序,直接从尾部取即可,而 \(S\) 中字典序降序,需要从头部取);
-
假如 \(\tt{}surname\) 的数量多于 \(\tt{}name\) ,并且待匹配数组 \(N\) 中有内容,那么优先将多余的 \(N\) 和 \(\tt{}surname\) 匹配(注意, \(\tt{}surname\) 中字典序升序,直接从尾部取即可,而 \(N\) 中字典序降序,需要从头部取);
-
完成前两轮操作后,若仍有 \(\tt{}name\) 和 \(\tt{}surname\) 可匹配,依次配对并放入答案数组 \(Ans\) ;
-
假如匹配完成后仍有多余的未匹配项,则直接放入对应的待匹配数组 \(N\) 和 \(S\) 。
最后,排序 \(Ans\) 数组并输出即可。
AC代码
点击查看代码
//====================
#define int LL
const int N = 1e6 + 7;
vector<string> name[N], surname[N], ans, _N, _S;
//====================
void Solve() {
int n; cin >> n;
for (int i = 1; i <= n; ++ i) {
string s; cin >> s;
name[s[0] - 'A'].push_back(s);
}
for (int i = 1; i <= n; ++ i) {
string s; cin >> s;
surname[s[0] - 'A'].push_back(s);
}
for (int i = 25; i >= 0; -- i) {
auto x = name[i], y = surname[i];
sort(x.begin(), x.end()), sort(y.begin(), y.end());
while (_N.size() != 0 && x.size() < y.size()) {
reverse(_N.begin(), _N.end());
ans.push_back(_N.back() + " " + y.back());
_N.pop_back(), y.pop_back();
reverse(_N.begin(), _N.end());
}
while (_S.size() != 0 && x.size() > y.size()) {
reverse(_S.begin(), _S.end());
ans.push_back(x.back() + " " + _S.back());
_S.pop_back(), x.pop_back();
reverse(_S.begin(), _S.end());
}
while (x.size() != 0 && y.size() != 0) {
ans.push_back(x.back() + " " + y.back());
x.pop_back(), y.pop_back();
}
while (x.size() != 0) {
_N.push_back(x.back());
x.pop_back();
}
while (y.size() != 0) {
_S.push_back(y.back());
y.pop_back();
}
}
sort(ans.begin(), ans.end());
for (int i = 0; i < n - 1; ++ i) cout << ans[i] << ", ";
cout << ans.back();
}
错误次数
(赛时 1 次)理解错题意。
(赛后 1 次)也是理解错了题意。
文 / WIDA
2022.03.25 成文
首发于WIDA个人博客,仅供学习讨论