• 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
    ");
                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
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    安装mysql Install/Remove of the Service Denied!错误的解决办法
    Oracle新建Schema
    TOMCAT虚拟路径配置
    Java的基本数据类型与转换
    _web基础_servlet基础
    布局的嵌套
    使用BootStrap网格布局进行一次演示
    BootStrap导入及其使用
    路由
    AngularJs MVC 详解
  • 原文地址:https://www.cnblogs.com/tiberius/p/11492821.html
Copyright © 2020-2023  润新知