Cry_For_theMoon  

ABC225F String Cards

题意:

给出 n 个小写字母串 s1,s2,...,sn,让你从中选出 k 个,然后把这个 k 个串重新排列后拼接得到一个字符串,要求是这个结果字符串的字典序最小。问这个结果串是多少。

kn50,|Si|50

分析:

这个题,是两个比较经典的贪心模型的结合,很有意思。

1. 给定 n 个串重新排列然后拼接,使得最后的字典序最小。

原题传送门 NOIP1998提高组 选数...

好的,我们还是正经分析一下。这种给一个排列让你重排的问题基本都是考虑邻项交换。交换的条件是很好想的,就是 si+sj<sj+si,但是我们得证明它是带传递性的。这里有一个很妙的想法:注意到长度相同的串比较字典序,等价于转换成 26 进制数后比较大小,越小的,字典序就越小(容易发现如果位数不同这个性质就不成立了,比如 cde)。那么我们不如设 si 写成数是 ti,那么等价于:

ti×26|sj|+tj<tj×26|si|+ti

然后我们移项以后会发现其实比较的就是:

ti26si1<tj26sj1

诶那这个式子的比较就肯定有传递性了。

2.给定 n 个串 s1,s2,...,sn,让你从中选 k 个串,按出现顺序拼接,使得字典序最小。

我们这个第一想法那就是 dp:设 f(i,j) 是前 i 个串,选了 j 个出来的最小字典序结果。但是这个其实是错误的。

一个误区是,因为考虑字典序从前往后,这里的 dp 也是从前往后考虑的。但是我们来看转移:dp(i,j)=min{dp(i1,j),dp(i1,j1)+si},这个其实有点问题,你看 min 中的第二项,它实质上是从后往前确定的:我确定末尾的结果,然后加上前面部分字典序最小的结果。

你可能还没有意识到问题,那就想想我们平时让字典序最小,是不是从第一位开始,让高位尽可能的小。换言之我们是先确定最开头的字符串,然后让后面部分字典序最小的。

所以应该倒着 dp,设 f(i,j) 是后 i 个串,选 j 个出来的最小字典序结果。

我们把 1,2 两个问题结合起来,就做完了:先用 1. 问题中的排序方法把 s 排序,然后执行 2. 问题中的 dp

事实上这种手法叫做 exchange-argument,想继续了解的可以直接去 CF 搜索。大致思想是,我们按照一个顺序排序,然后进行 dp

代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=60;
ll n,k;
string s[MAXN];
string f[MAXN][MAXN],inf;
bool cmp(const string& s1,const string& s2){
    return s1+s2<s2+s1;
}
int main(){
    cin>>n>>k;
    rep(i,1,n)cin>>s[i];
    inf+=('z'+1);
    sort(s+1,s+1+n,cmp);
    rep(i,1,k)f[n+1][i]=inf;
    per(i,n,1)rep(j,0,k){
        f[i][j]=f[i+1][j];
        if(j)f[i][j]=min(f[i][j],s[i]+f[i+1][j-1]);
    }
    cout<<f[1][k]<<endl;
    return 0;
}
posted on   Cry_For_theMoon  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
 
点击右上角即可分享
微信分享提示