【题解】ABC225 F - String Cards
思维不严谨被毒打了。(每日一个挂分小技巧
一个比较经典的 trick,重载小于号 \(a<b\) 为 \(a+b < b+a\),最后答案序列一定是满足 \(S_{p_i} \le S_{P_{i + 1}}\) 。
然后我们直接 DP 即可,设 \(f_{i,j}\) 表示最后一个串是 \(S_i\),用了 \(j\) 个串的最小字典序,时间复杂度 \(\mathcal{O}(N^3|S|)\)。
交了之后发现 WA 了三个点一直过不去。赛后才发现这个方法有一个致命的 bug。
由于我们的状态是答案串的前 \(j\) 个串拼接的最小字典序,但是字典序最小时,存在一个方案是另一个方案的前缀的情况,而对于这种情况,算法只能目光短浅的考虑长度较短的情况。例如以下这个 Hack 数据
4 3
bba
bbaba
b
b
所以我们应该倒着 DP,状态 \(f_{i,j}\) 表示当前第一个串是 \(S_i\) ,答案串最后 \(j\) 个串的最小字典序。这样我们 DP 时,对于一个方案是另一个方案前缀的情况,一定选择最短的,因为后面不会再接新的串,方案一定最优。
#define N 55
int n, k;
string a[N];
string f[N][N];
bool cmp(string x, string y){return x + y < y + x || (x + y == y + x && x.length() < y.length());}
int main() {
//int T = read();while(T--)solve();
n = read(), k = read();
rp(i, n)cin >> a[i];
sort(a + 1, a + n + 1, cmp);
rep(i, 1, n + 1)rep(j, 0, n)f[i][j] = "~";
f[n + 1][0] = "";
pre(i, n, 1){
rep(j, i + 1, n + 1)rep(p, 0, n + i - j){
f[i][p + 1] = min(f[i][p + 1], a[i] + f[j][p]);
}
}
rep(i, 1, n)cout << "ss " << a[i] << endl;
string ans = "~";
rep(i, 1, n - k + 1)ans = min(ans, f[i][k]);
cout << ans << endl;
return 0;
}