这个比赛的全称是ICPC North America Championship 2021

先贴个题目链接 J-The Paladin

这道题目题意说得非常云里雾里,简而言之就是用输入的那几个以2为长度的字符串组合成一个长的回文.具体而言,这些小的字符串得两两在中间相连,而且每种小字符串都有所谓的费用,要求是使得费用最小.

容易看出,这道题得用DP

好 既然知道了要用DP,那么应该如何动态规划呢?要点就是找状态量和状态转移方程.

考虑到只要一个回文字符串左边出现了ab,则右边就必然出现ba,那么我们可以只研究这个字符串的一半:

因此可以得到状态转移方程:

const int maxalpha = 26;
const int maxcost = 1e5;
const int maxk = 105;

中,表示第i个字符,表示在这第i个字符上选择这个字母.整体表示在这第i个字符上选择这个字母的最小的费用.

表示子字符串ij的费用,输入中没给出的一律当作maxcost

最后答案就是中间那个字符上选择任意一个字母的最小值.即

到这儿,其实这道题目的解法就已经大部分出来了.


下面是一些小小的细节:

1. 对于k为奇数,那么根据我们只研究一半的字符串,只需要这个dp状态转移方程递推k/2次即可.

2. 对于k为偶数,根据多次的手操模拟发现只需要k/2-1次即可,前k/2-2次跟上面给出的那个状态转移方程完全一样,

最后一次略有不同, 为了更好地说明这种情况,可以想象abaaba中第3个字符----a,添加这个字母就会增加ba,ab,aa三个小字符串的费用.所以最后一次的状态转移方程应当是

3. 对于k=2,需要特判一下,也就是只能像aa,bb,cc这样的填进去.

最后得出答案的时候,如果最小值是maxcost,那么得输出-1才行.


以下为AC代码,其中的ijk下标代表的意义可能与以上说明有所不同.但是道理是一样的.

时间复杂度大约为

#include <bits/stdc++.h>
 
using namespace std;
 
const int maxalpha = 26;
const int maxcost = 1e5;
const int maxk = 105;
 
int cost[maxalpha][maxalpha]{};
int dp[maxk][maxalpha]{};
 
int main()
{
    int n, k;
    cin >> n >> k;
    string temp;
    fill(*cost, *(cost + maxalpha), maxcost);
    for (int i = 1, c; i <= n; i++)
    {
        cin >> temp >> c;
        cost[temp[0] - 'a'][temp[1] - 'a'] = c;
    }
    if (k % 2 == 1)
    {
        for (int t = 1; t <= k / 2; t++)
        {
            for (int i = 0; i < maxalpha; i++)
            {
                int temp = maxcost;
                for (int j = 0; j < maxalpha; j++)
                {
                    temp = min(temp, dp[t - 1][j] + cost[j][i] + cost[i][j]);
                }
                dp[t][i] = temp;
            }
        }
    }
    else
    {
        if (k == 2)
        {
            for (int i = 0; i < maxalpha; i++)
            {
                dp[k / 2][i] = cost[i][i];
            }
        }
        else
        {
            for (int t = 1; t <= k / 2 - 2; t++)
            {
                for (int i = 0; i < maxalpha; i++)
                {
                    int temp = maxcost;
                    for (int j = 0; j < maxalpha; j++)
                    {
                        temp = min(temp, dp[t - 1][j] + cost[j][i] + cost[i][j]);
                    }
                    dp[t][i] = temp;
                }
            }
            for (int i = 0; i < maxalpha; i++)
            {
                int temp = maxcost;
                for (int j = 0; j < maxalpha; j++)
                {
                    temp = min(temp, dp[k / 2 - 2][j] + cost[j][i] + cost[i][j] + cost[i][i]);
                }
                dp[k / 2][i] = temp;
            }
        }
    }
    int asw = *min_element(*(dp + k / 2), *(dp + k / 2 + 1));
    if(asw == maxcost) cout << -1 << endl;
    else cout << asw << endl;
    return 0;
}