DTOJ 2435:回文串游戏(one)

DTOJ 2435:回文串游戏(one)

【题目描述】
      小承和黄神都很喜欢玩游戏,有一次小承给黄神一个游戏,而黄神是脱团狗忙于(哔)不想花太长时间,就扔给了你让你以最快速度解决。
      游戏是这样的,给你一个长度为n的字符串S(编号是1,2,…,n),你一开始在位置p,你每次操作可以选择向左移动一格,向右移动一格或者将当前位置字符改变一个,改变一个的含义是可以将字符串x变成字符串x+1(即将a改成b,b改成c,特别的z改成a),也可以将字符x变成x-1(即将z改成y,y改成x,特别的,a改成z),当然如果在1的位置向左会移动到n,同时在n时向右移动会移动到1,你要将S变成回文串。请问你至少要经过多少次操作才能完成游戏?
【输入】
      第一行两个用一个空格隔开的正整数np
      第二行一个长度为n的字符串,保证只含有小写字母。
【输出】
      一行一个整数表示答案。
【样例输入】
8 3
aeabcaez
【样例输出】
6
【分析】
      本题的答案由两部分组成,一部分是修改操作的次数,另一次则是移动操作的次数,可以分开计算。
      先考虑修改操作。将一个字母修改为另一个字母,有两种方案。一种是向上每次+1,一种是向下每次-1。设两个字母为a,b(a<b),不难得出,两种方案所需的代价为b-a和a-b+26,则对于每一位与其回文位置的一位,取两者中的较小值即可。
      接下来考虑移动操作。容易知道如果将整个字符串翻转,答案是不会变的,于是可以将初始位置翻转到前一半。设L为前一半下标最小的需要修改的位置的下标,R为前一半下标最大的需要修改的位置的下标,则前一半需要修改的位置全部落在[L,R]之间。不难发现,此时的最优解一定是先移动到其中一端,在便利整个区间。
      最后的答案就是两种操作的次数之和。

【代码】

#include<bits/stdc++.h>
#define d(a,b) (min(abs(a-b),26-abs(a-b)))
using namespace std;
const int maxn=(100000+10)>>1;
int n,p,m,ans,l=maxn,r=-1,f;
char s1[maxn],s2[maxn];
int main()
{
    scanf("%d %d",&n,&p);m=n>>1;
    if ( n==1 ) return !putchar('0');
    if ( p>m ) p=n-p+1;
    for ( int i=1;i<=m+(n&1);i++ ) scanf(" %c",&s1[i]);
    for ( int i=m;i;i-- ) scanf(" %c",&s2[i]);
    for ( int i=1;i<=m;i++ )
        if ( s1[i]!=s2[i] ) f=1,l=min(l,i),r=max(r,i),ans+=d(s1[i],s2[i]);
    if ( !f ) return !putchar('0');
    return !printf("%d",ans+abs(r-l)+min(abs(p-l),abs(r-p)));
}

猜你喜欢

转载自blog.csdn.net/dtoi_rsy/article/details/80935290
今日推荐