• codeforces gym 100345I Segment Transformations [想法题]


    题意简述

    给定一个由A C G T四个字母组成的密码锁(每拨动一次 A变C C变G G变T T变A)

    密码锁有n位 规定每次操作可以选取连续的一段拨动1~3次

    问最少几次操作可以将初始状态变到末状态

    并且把每次操作输出

    (此题有spj)

    ---------------------------------------------------------------------------------------------------------

    为了方便叙述 所有没有明确说明的位置均是在$mod4$意义下的

    首先我们显然可以明白一个性质 操作顺序是没关系的

    有关系的只是每个位置被拨动的次数

    比赛的时候一开始想的是比较随意的贪心 然而是有反例的

    最后剩下30min的时候 从数据范围想到了区间Dp

    然而这题并不是一般的区间Dp

    最小操作数好求然而操作方案难以记录

    补题的时候 最终又去想象有没有什么更好的贪心思路

    ---------------------------------------------------------------------------------------------------------

    我们用一个高度数组h记录从初始状态到末状态每个位置需要拨动的次数

    比如样例

    AGGTCAT

    AAACTAA

    高度数组h便是 0222201

    我们再定义一个delta数组 代表所有的$h[i]-h[i-1]$

    那么从$1$到$n+1$delta数组的值便是 02000213

    (这样的构造类似与用树状数组维护区间加减值的做法 不过这种思想的具体名称我也不知道)

    显然我们每次最优可以将两个2变为0 或者将一个1和一个3变为0

    于是这样就可以做了?

    然而只是这样做的话会RE11

    ---------------------------------------------------------------------------------------------------------

    比如这样一个样例

    AA

    GT

    高度数组h为 23

    delta数组为 211

    于是并不能找出两个2或者一个1一个3来配对消除

    既然无法一次消两个 我们就一次消一个吧

    不过显然是不能使一些已经消除的部分又出现

    所以直接找两个非0的进行处理 把其中一个变为0就好了

    (注意到delta数组之和为0 所以最后一定不会只剩下一个非0的)

    #include <bits/stdc++.h>
    using namespace std;
    const int N=110;
    char s1[N],s2[N];
    int h[N],delta[N],cnt[4];
    int L[N],R[N],d[N];
    int n,ans;
    int main()
    {
    #ifdef ONLINE_JUDGE
            freopen("transform.in","r",stdin);
            freopen("transform.out","w",stdout);
    #endif
            scanf("%s%s",&s1[1],&s2[1]);
            n=strlen(&s1[1]);
            for(int i=1;i<=n;++i)
            {
                if(s1[i]=='A')
                    h[i]=-0;
                else if(s1[i]=='C')
                    h[i]=-1;
                else if(s1[i]=='G')
                    h[i]=-2;
                else
                    h[i]=-3;
                if(s2[i]=='A')
                    h[i]+=0;
                else if(s2[i]=='C')
                    h[i]+=1;
                else if(s2[i]=='G')
                    h[i]+=2;
                else
                    h[i]+=3;
                h[i]=h[i]<0?h[i]+4:h[i];
            }
            for(int i=1;i<=n+1;++i)
            {
                delta[i]=(h[i]-h[i-1]+4)%4;
                cnt[delta[i]]++;
            }
            while(cnt[0]!=n+1)
            {
                ++ans;
                int i=1;
                while(!delta[i])
                    ++i;
                int j=i+1;
                while(delta[j]+delta[i]!=4&&j<=n+1)
                    ++j;
                if(j<=n+1)
                {
                    L[ans]=i;
                    R[ans]=j-1;
                    d[ans]=delta[i];
                    cnt[delta[i]]--;
                    cnt[delta[j]]--;
                    cnt[0]+=2;
                    delta[i]=delta[j]=0;
                }
                else
                {
                    j=i+1;
                    while(!delta[j])
                        ++j;
                    L[ans]=i;
                    R[ans]=j-1;
                    d[ans]=delta[i];
                    cnt[delta[i]]--;
                    cnt[delta[j]]--;
                    cnt[0]++;
                    cnt[(delta[j]+delta[i])%4]++;
                    delta[j]=(delta[j]+delta[i])%4;
                    delta[i]=0;
                }
            }
            printf("%d
    ",ans);
            for(int i=1;i<=ans;++i)
                printf("%d %d %d
    ",L[i],R[i],d[i]);
        return 0;
    }
  • 相关阅读:
    BackgroundWorker
    Devexpress 10
    Winform常用控件的使用(一)
    Composite UI Application Block(CAB)
    HTML CSS简单总结
    C# JSON字符串序列化与反序列化常见模型举例
    HTML 调用iscroll.js主要事项
    HTML5 Socket通信
    Webservice加上SoapHeader验证方式
    动态调用Webservice 支持Soapheader身份验证(转)
  • 原文地址:https://www.cnblogs.com/sagitta/p/4770389.html
Copyright © 2020-2023  润新知