P80023 [CSP-J二十连测第六套 ] --T3--回文(palindrome)
最小回文变换代价
https://www.mxoj.net/problem/P80023
总体思路:
1、先精简问题,考虑不交换,只修改情况下,代价是多少
2、再精简,只考虑修改其中一对对称字母的最小代价是多少
// 计算一对字母 (x,y) 的最小代价
LL calc(int x, int y) {
if (x == y) return 0; // 相同字母无需代价
// 三种方式取最小:
// 1. 把 y 变为 x,代价 v[x]
// 2. 把 x 变为 y,代价 v[y]
// 3. 把两者都变为某个字母,代价 mn = min(2*v[c])
return min({v[x], v[y], mn});
}
那不交换情况下代价就是所有字母对修改代价和。
直接枚举字符串n太大。转化一下,统计不同字母对个数。这样就能更快计算出代价和。
3、考虑交换情况
暴力枚举所有字母对,计算交换代价
代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int n;
LL v[27]; // 每个字母变换到它的代价
LL mn; // 全局最小的 2*v[i](表示两边都变成某个字母的代价下界)
int t[1000005]; // 字符串转化为数字(1~26)
LL ans, minans;
LL a[27][27]; // a[i][j] 记录字母对 (i,j) 出现次数 (i <= j)
// 计算一对字母 (x,y) 的最小代价
LL calc(int x, int y) {
if (x == y) return 0; // 相同字母无需代价
// 三种方式取最小:
// 1. 把 y 变为 x,代价 v[x]
// 2. 把 x 变为 y,代价 v[y]
// 3. 把两者都变为某个字母,代价 mn = min(2*v[c])
return min({v[x], v[y], mn});
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
mn = 1e18;
// 读入字母代价
for (int i = 1; i <= 26; i++) {
cin >> v[i];
mn = min(mn, 2 * v[i]); // 记录最小的 2*v[i]
}
string s;
cin >> s;
s = " " + s; // 下标从 1 开始,方便处理
// 转换字符串为数字序列
for (int i = 1; i <= n; i++) {
t[i] = s[i] - 'a' + 1;
}
// 统计所有对称位置的字母对
for (int i = 1; i <= n / 2; i++) {
int x = t[i], y = t[n - i + 1];
if (x > y) swap(x, y); // 保证 i<=j 存储
a[x][y]++;
}
// 计算不交换情况下的总代价
ans = 0;
for (int i = 1; i <= 26; i++) {
for (int j = i; j <= 26; j++) {
ans += calc(i, j) * a[i][j];
}
}
minans = ans; // 初始最优代价为“不交换”
// 枚举交换两个字母对的情况
for (int i = 1; i <= 26; i++) {
for (int j = i; j <= 26; j++) {
if (!a[i][j]) continue; // 没有这种字母对
a[i][j]--; // 拿出一个 (i,j) 对
for (int k = 1; k <= 26; k++) {
for (int l = k; l <= 26; l++) {
if (!a[k][l]) continue; // 没有这种字母对
// 原来的代价是 calc(i,j)+calc(k,l)
// 交换后可能变为 (i,l)+(j,k) 或 (i,k)+(j,l)
minans = min(minans,
ans - calc(i, j) - calc(k, l)
+ calc(i, l) + calc(j, k));
minans = min(minans,
ans - calc(i, j) - calc(k, l)
+ calc(i, k) + calc(j, l));
}
}
a[i][j]++; // 放回 (i,j)
}
}
cout << minans << "\n";
return 0;
}
代码逻辑总结
所以,这份代码的核心亮点在于:
把所有对称位置压缩为频率表,避免了对 n 的直接操作。
只需要枚举所有可能的字母对组合,就能找到“最优交换”效果。
因为字母只有 26 个,复杂度几乎是常数,非常优雅。

浙公网安备 33010602011771号