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个人博客,仅供学习讨论


posted @ 2022-03-26 09:00  hh2048  阅读(65)  评论(0编辑  收藏  举报