POJ1854

题意略。

思路:

贪心的想法是:

每次搞定最左边和最右边的两个字母,也就是从外向内一层层做成回文串。

比如 abcbac 这个,先看最左边的“a”,从最右边开始遍历字符串,找到的第一个“a”就可以经过最少次数把右边变成“a”,

再看最右边的“c”,同样的,从最左边遍历字符串,找到的第一个“c”就可以经过最少次数把右边变成“c”,

比较一下是把最外层变成两个a还是两个c……哪个划算,就加上这个步数,把字符串最外层排好:“abcbca”,then去掉最外层变成:“bcbc”,继续重复上面的工作……

 我在做的时候想到了一个问题,如果当前最贪心的解既不在最左,也不在最右,而是中间的某个字母,此时我的贪心方法还正确吗?

我这里拿a,b举例:

假设a是最左边的,且我令取右端的永远不如取左端的优,而b是从a开始向右扫描第一个swap数比a小的

我假设左端的a到b之间有i个字母(左闭右开),任取其中一个字母,记它的下标为j,以它作为区间两端的swap数为Kj,可知a的swap数为K1。

再记b作为区间两端的swap数为X,那么我们从a一直处理到b这个过程中我的swap总数为:

K1 + K2 + ...... + Ki + X - i - w0 - w1

这里说明一下:

我在从a处理到b之前的这i个字母swap的总数并不是K1 +... + Ki了,因为区间两端点一直在变化,实际的数字比这个加和要小,

由于左端点向中间靠近所带来的减损我们记为w0,由于右端点向中间靠拢所带来的减损我们记为w1。

注意,只要我是从1到i这个顺序来处理,那么这两个数字是不会发生变化的。

还有一个要知道的是,这i个字母它们在右端(从右向左扫描)的第一个相同字母一定在右端b的左边。

如果在右边,那么b作为“从a开始向右扫描第一个swap数比a小的”这么一个条件也就不成立了。

我们再来考虑一下处理前 i 个字母对b的影响:

首先,对于左端的b,它距离左端点的长度一直在减小,因此我们要 - i来表示。

对于右端点的b,它距离右端点的值始终没有变化,因为不断往中间靠拢的右端使用的字母来自b的左端。

因此上式成立。

 

如果我们此时先处理左端的b,再来处理1...i的话,由于b在1 ... i的右端,所以它对K1 + ... + Ki不会有额外的影响,

但是由于右端的b一定在这1...i这i个字母于右端相应位置的右边,所以它对这i个字母统一有一个右端点向中间靠拢1个单位的影响,

因为有i个字母,所以共计影响应该 - i。

这种情况下的swap总数记为:K1 + K2 + ...... + Ki + X - i - w0 - w1。

与上种策略一样,所以处理两端的是最优的。

 

但为什么要优先处理两端中swap数最小的呢?

因为越小说明它越能比别的字母靠近两端,越靠近两端,它带来的总体减损值也就越多。

 

代码附上:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 8005;
const int maxn0 = 505;

int cnt[maxn0],T;
char str[maxn];

int main(){
    scanf("%d",&T);
    while(T--){
        memset(cnt,0,sizeof(cnt));
        scanf("%s",str);
        int len = strlen(str);
        for(int i = 0;i < len;++i) ++cnt[str[i]];
        int odd = 0;
        for(int i = 'a';i <= 'z';++i) odd += (cnt[i] & 1);
        if(odd > 1){
            printf("Impossible\n");
            continue;
        }
        
        int ans = 0;
        int lft = 0,rht = len - 1;
        while(lft < rht){
            int ptr0,ptr1,cost0,cost1;
            for(ptr0 = rht;ptr0 > lft && str[ptr0] != str[lft];--ptr0);
            for(ptr1 = lft;ptr1 < rht && str[ptr1] != str[rht];++ptr1);
            cost0 = ptr0 == lft ? maxn : rht - ptr0;
            cost1 = ptr1 == rht ? maxn : ptr1 - lft;
            
            if(cost0 < cost1){
                for(int i = ptr0;i < rht;++i) swap(str[i],str[i + 1]);
            }
            else{
                for(int i = ptr1;i > lft;--i) swap(str[i],str[i - 1]);
            }
            ans += min(cost0,cost1);
            lft += 1;
            rht -= 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2019-09-09 17:09  温和的提比略  阅读(208)  评论(0编辑  收藏  举报