题解: AT_abc225_f_String Cards

题解: AT_abc225_f_String Cards

AT_abc225_f_String Cards
贪心+DP

分析

首先考虑一个类似的问题:Luogu_P1012 [NOIP 1998 提高组] 拼数

解决这个问题的关键在于一种特殊的排序函数:

bool cmp(string A,string B)
{
    return A+B<B+A;
}

这个结论成立的前提是:如果 \(A<B\) 那么 \(A+C<B+C\),显然对于数字成立。

排完序后选择的前后位置也就被确定了下来,这道题多出了一个选 \(k\) 个的条件,是不是意味着我们排完序后直接从前往后 DP 就行了?

状态:设 \(f_{i,j}\) 表示将排序后前 \(1\sim i\) 的串选 \(j\) 个拼在一起的字典序最小的串。
边界:\(f_{0,0}=0\),其余为无穷大。
目标:\(f_{n,k}\)
转移:\(f_{i,j}=min(f_{i-1,j},f_{i-1,j-1}+st[i])\),注意到 \(st_i\) 要拼在后边,满足排序后的相对顺序。

别急,看组数据:

3 2
bba
bb
bba

我们被 hack 了,思考原因,注意到数字的大小比较是右对齐进行比较的,因此从左数的长度不影响,因此上一题的前提成立;而字典序是左对齐进行比较的,因此我们应该从右往左 DP。

即字典序存在另一个性质:如果 \(A<B\),那么 \(C+A<C+B\)

状态:设 \(f_{i,j}\) 表示将排序后前 \(i\sim n\) 的串选 \(j\) 个拼在一起的字典序最小的串。
边界:\(f_{n+1,0}=0\),其余为无穷大。
目标:\(f_{1,k}\)
转移:\(f_{i,j}=min(f_{i+1,j},st[i]+f_{i+1,j-1})\),注意到 \(st_i\) 要拼在边,满足排序后的相对顺序。

代码

//AT_abc225_f

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 55;

int n, k;
string st[N];

struct node
{
    vector <int> vec;

    inline string trans()
    {
        if (vec.empty())
            return "{}";
        string res;
        for (int i = 0; i < vec.size(); i ++)
            res += st[vec[i]];
        return res;
    }

    inline void ins(int pos, int val)
    {
        vec.insert(vec.begin() + pos - 1, val);
    }

    inline void del(int pos)
    {
        vec.erase(vec.begin() + pos - 1);
    }

    inline friend bool operator < (node a, node b)
    {
        return a.trans() < b.trans();
    }
} f[N][N];

bool cmp(string a, string b)
{
    return a + b < b + a;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    cin >> n >> k;
    for (int i = 1; i <= n; i ++)
        cin >> st[i];
    sort(st + 1, st + n + 1, cmp);

    for (int i = n; i >= 1; i --)
        for (int j = 1; j <= min(k, n - i + 1); j ++)
        {
            f[i][j] = f[i + 1][j];
            f[i + 1][j - 1].ins(1, i);
            f[i][j] = min(f[i][j], f[i + 1][j - 1]);
            f[i + 1][j - 1].del(1);
        }

    cout << f[1][k].trans() << endl;

    return 0;
}
posted @ 2025-02-07 08:05  nueryim  阅读(21)  评论(0)    收藏  举报