AT_code_festival_2017_qualb_f Largest Smallest Cyclic Shift 题解
题目链接:Largest Smallest Cyclic Shift
题意:
定义 $f(S)$ 为:对于一个字符串 $S$,每次将它最左边的字符放置到字符串末尾生成的字符串集合中,字典序最小的字符串。例如:对于 $S$ 为
babca
的情况,$f(S)$ 即为babca
、abcab
、bcaba
、cabab
、ababc
中最小的那个,即ababc
。你需要构造一个字符串 $T$,共包含 $X$ 个字符
a
、$Y$ 个字符b
和 $Z$ 个字符c
,使得 $f(T)$ 尽可能大,输出这个 $f(T)$。数据范围:$1 \le X + Y + Z \le 50$ 。
非常有趣的贪心题。注意到 $X=Y=Z$ 时,最优方案是 $\tt abc$ 。
考虑递归的构建字符串。设 $s_1,s_2,s_3$ 是当前最优的三个字符串,规定 $s_1 < s_2 < s_3$ 。
显然我们会选择 $s_1s_3s_2$ 的形式,因为在 $s_1,s_2,s_3$ 最优的情况下你怎么转这个都是最优的。
若此时再加入一个 $s_4 > s_3$ ,考虑把它放到 $s_3$ 的前面,即 $s_1s_4s_3s_2$ ,也可以证明是最优的。
有趣的是,这种情况下,相当于不断地把当前最小和最大的合并,即 $s_1s_4$ ,然后 $s_1s_4s_3$ ,最后加上 $s_2$
这使得我们考虑,是否对于任意的 $k$ ,最优方案为 $s_1s_k \cdots s_2$ 。可以证明这是正确的。
证明 考虑归纳法证明。假设对于某个 $k$ ,小于等于 $k$ 的情况均成立,考虑 $k+1$ 的情况。
对于串 $s_{k+1}$ ,此时 $s_1$ 依然在最前面,而因为 $s_{k+1} > s_k$ ,所以 $s_k$ 后面的串都是小于 $s_{k+1}$ 的
如果我们把 $s_k\cdots s_2$ 看作一个整体,那么这个串就是原来的 $s_2$ ,而 $s_{k+1}$ 相当于 $s_3$ 。
那么我们可以用一个 multiset<string>
来维护这个东西,每次取出当前的最小值和最大值合并即可。
时间复杂度 $\mathcal{O}(n^2 \log n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)())
multiset<string> s; int a,b,c;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> a >> b >> c;
while(a--) s.insert("a");
while(b--) s.insert("b");
while(c--) s.insert("c");
while(s.size() > 1)
{
auto x = s.begin(), y = prev(s.end());
s.insert((*x) + (*y)); s.erase(y); s.erase(x);
}
cout << *s.begin() << '\n';
return 0;
}
参考文献: