AtCoder Beginner Contest 286—C—Rotate and Palindrome

题目链接:

题意:给我一个长度为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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值