Link.
Description.
有 \(n\) 个字符串,选出 \(K\) 个按任意顺序连接。
求字典序最小的方案。
\(1\le k\le n\le 50\)
Solution.
首先,当 \(K=n\) 的情况是一个经典原题。
可以直接 sort(s.begin(),s.end(),[](string a,string b){return a+b<b+a;});
就做完了。
这个满足偏序也很好证明,可以直接算哈希值。
这样就有 \(a\times 26^{|B|}+b\le b\times26^{|A|}+a\),有 \(\frac{a}{26^{|A|}-1}\le \frac{b}{26^{|B|}-1}\)
同时,排完序后,选出来 \(K\) 个肯定会按原序列顺序排序,证明显然。
然后,\(K\ne n\) 的时候考虑 dp。
dp 要求的是全局最优决策的部分肯定是局部最优决策。
从前往右肯定有问题,因为 a
和 aa
局部最优决策是 a
,但是后面连一个 b
就要选 aab
。
考虑从后往前,往前接一个相同的字符串,后面肯定字典序越小越好。
所有从后往前 dp 就行了。
Coding.
点击查看代码
//Coded by leapfrog on 2021.10.29 {{{
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
string s[55],dp[55][55];int n,K;
int main()
{
read(n,K);for(int i=1;i<=n;i++) cin>>s[i];
sort(s+1,s+n+1,[](string a,string b){return a+b<b+a;});
for(int i=n;i>=1;i--)
{
dp[i][n-i+1]=s[i]+dp[i+1][n-i];
for(int j=1;j<=n-i;j++) dp[i][j]=min(dp[i+1][j],s[i]+dp[i+1][j-1]);
}return cout<<dp[1][K]<<endl,0;
}