题目链接:
题意:给我一个长度为N的字符串S,你可以执行以下两种操作,0次或者更多次,以任意的顺序执行!
1、支付A元,移动S的最左边的字符到最右边,换句话说,
就是 S1,S2…Sn —> S2,Sn, S1;
2、支付B元,在 1~n之间选择一个整数 i,然后替换字符串S中的第 i 个字符Si,为任意的小写英文字母。
你最少需要支付多少日元,才能使得S变成一个回文串?
思路:
既然题目已经告知了操作1和操作2顺序可以交叉进行,那么应该以何种顺序去进行操作呢?
这时候,我们可以从两种边界情况出发;
边界1:只进行第一种操作:假设当前有一个字符串,abcd
abcd 第一次操作1 bcda
bcda 第二次操作1 cdab
cdab 第三次操作1 dabc
dabc 第四次操作1 abcd
不难发现操作一最多 3次,因为3次以内如果还没有变成回文串的话,则永远不可能通过操作1实现,而这里隐藏着一个重要的性质,假设字符串的长度为n,则操作1最多进行n-1次!
边界2:只执行操作2:假设当前有一个字符串 abcd
那么开始执行操作2:
abcd 第一次 abca
abca 第二次 abba
显然,只只需要执行 2次就可以变成一个回文串,这里也存在一个规律,即执行操作2,能够使得其变成一个回文串,字符串长度为n,最多执行 n/2次!
那么我们可以考虑操作1和操作2交叉进行的情况,即将两种边界情况都考虑进去:操作1,可以枚举字符串的 n-1种形态,上面已经给出了,如下所示:
abcd bcda cdab dabc
那么我们就每次进行一次操作1,我们就进行操作2,即看上面四种情况里面,哪种情况变成回文串的花费最小即可!
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 5e3 + 10;
int n, a, b; //a,b的数据范围都是 [1, 1e9],并且后续涉及了a和b的多次相加相乘,所以会爆int,所以这里开 long long!
char str[N];
int main()
{
cin >> n >> a >> b;
scanf ("%s", str+1);
//因为操作1 的缘故会使得字符串陷入一个循环,这里预处理将字符串进行一个复制
//abcd --> bcda --> cdab --> dabc --> abcd
//效果:abcd ---> abcdabcd;
for (int i=n+1; i <= 2*n; i ++)
str[i] = str[i-n];
LL res = 9e18;
for (int i=0; i < n; i ++) //枚举操作1,总共n-1次!
{
LL now = i*a; //保留当前花费为多少,记录当前是第几次a操作!
int l = i+1, r = i+n; //abcd --> bcda --> cdab --> dabc; l和r是每次操作1变换后的字符串的左右边界!
for (int j=1; j <= n/2; j ++) //枚举操作2使得s变成的回文串的操作次数以及对应的花费!
if (str[l+j-1] != str[r-j+1]) //往中间逼近!
now += b; //每进行一次操作2,就累加一次操作2的花费!
res = min (now, res);
}
cout << res;
return 0;
}