zoukankan      html  css  js  c++  java
  • [2020 小米邀请赛][E. Rikka with Subsequence]

    因为这题错失了1k RMB+小米手表,心塞塞...

    题目链接:https://ac.nowcoder.com/acm/contest/9328/E

    题目大意:给定一个数字(x),(x)的长度不超过(5000)位,设有两个数(a,b)满足(a+b=x),(a)与(b)的字符串形式的最长公共子序列为(c),要求输出一个使(c)的长度尽可能长的方案

    题解:记(|x|)表示数字(x)的长度,考虑(|c|)的理论最大值

       显然,当且仅当(x[0]>1)且(x)为偶数时,可以取到(|c|=|x|)

       若(x)为偶数,且(|c| eq |x|),那么此时一定有(x[0]=1),可以得出最优解是(a=b=frac{x}{2}),此时(|c|=|x|-1)

       若(x)为奇数,那么一定有(|c|le |x|-1),假设有(b=10a+k(k<10))使得(a+b=x),那么可以发现当(x)对(11)取模的值不为(10)时,有(|c|=|a|)的解,其中,(a=lfloor frac{x}{11} floor )。于是可以发现,在这种情况下,当(x)的前两位的值大于等于(11)时,可以取到(|c|=|x|-1),否则会有以下两种情况:

        (x)为个位数,此时可以特判

        (x)是(10)开头的,可以发现,这个时候(|a|)与(|b|)的取值只可能是:(|a|=|x|, |b|le |x|-2)或(|a|=|x|-1, |b|le |x|-1)(这里不妨设(|a|ge|b|))。在第一种情况下,有(|c|le |x|-2),在第二种情况下,由于(x)为奇数,如果(|c|=|x|-1=|a|)则会推出(a=b),从而导致矛盾。因此,此时的最优解只可能是(|c|=|x|-2),同样取(a=lfloor frac{x}{11} floor )也可以得到最优解。

       剩下(x)为奇数,且(x equiv 10 (mod 11))的情况。一开始我的思路如下(可略过):


       此时,若(x)的最后一位不为(9)或者(x)的倒数第二位为偶数,则可以令(a=lfloor frac{x}{2} floor, b=a+1),此时有(|c|=|a|-1)。当(x[0]>1)时,(|c|=|x|-1),否则当(x[0]=1)时,考虑是否还有让(|c|=|x|-1)的可能

       在这种情况下,考虑(|a|)与(|b|)的取值,可以发现,有(|a|=|x|, |b|le |x|-1)或(|a|=|x|-1, |b|le |x|-1)。显然由于(x)为奇数,第二种情况下不能取到最优解。而在第一种情况下,由于要取到(|c|=|x|-1),因此(a)只可能是(b)中间再塞进去一位。而又因为(x)是奇数,所以这一位只可能是最后一位(否则两个数个位相同相加为偶数),故只可能是(b=10a+k(k<10))的形式。但此时已有结论(x equiv 10 (mod 11)),故只能让(|c|=|x|-2),令(a=lfloor frac{x}{2} floor, b=a+1)即可

       最后只剩下(x)为奇数,(x equiv 10 (mod 11)),且(x)的末两位为(y9)((y)为奇数)的情况。如果(x)的倒数第三位为偶数,则可以将前面的部分和后面这两位分开,其中前面的部分除以二,后面的部分按照模(11)小于(10)来处理,这样可以证明是最优解(证明方法同上,此时(|c|=|x|-1)或(|x|-2))

       若(x)的倒数第三位为奇数,则可以让前面的部分除以二向下取整,末尾分别拼上二位数(p,q)使得(p+q=1y9),且(p)与(q)有一位相同。这时会发现除了(y=9)其余情况均能处理,于是考虑换一种思路


       如果(x)的最后一位不为(9),同样令(a=lfloor frac{x}{2} floor, b=a+1)。否则,则有(str(x)=str(y)+str(c)+str(99...9)),若(y)为偶数,则令(str(a)=str(frac{y}{2})+str(c)+str(0909..09),str(b)=str(frac{y}{2})+str(0)+str(9090..90))可得最优解(对于(x[0]=1)的情况讨论不再赘述,其实可以发现若(x[0]=1),(x)为奇数,且(x equiv 10 (mod 11)),(|c|le |x|-2)一定成立)。若(y)为奇数,则令(str(a)=str(frac{y-1}{2})+str(c+1)+str(9090..90),str(b)=str(frac{y-1}{2})+str(9)+str(0909..09))即可,需要注意的是如果(y=1)则不能将前导零输出

    #include<bits/stdc++.h>
    using namespace std;
    #define N 5050
    int T,n;
    char s[N],t[N];
    void Div(int n,int x)
    {
        int m=strlen(t);
        memset(t,0,sizeof(char)*(m+5));
        int r=0,j=0;
        for(int i=0;i<n;i++){
            r=(r*10+s[i]-'0');
            if(r>=x || j>0){
                t[j++]=r/x+'0';
                r%=x;
            }
        }
        if(j==0)t[j++]='0';
    }
    void init()
    {
        scanf("%s",s);
        n=strlen(s);
        if(s[n-1]%2==0){
            Div(n,2);
            printf("%s
    %s
    %s
    ",t,t,t);
            return;
        }
        if(n==1){
            printf("%d
    %d
    -
    ",(s[0]-'0')/2,(s[0]-'0')/2+1);
            return;
        }
        int x=0;
        for(int i=0;i<n;i++)x=(x*10+s[i]-'0')%11;
        if(x<10){
            Div(n,11);
            printf("%s%d
    %s
    %s
    ",t,x,t,t);
            return;
        }
        if(s[n-1]!='9'){
            Div(n,2);
            int m=strlen(t);
            printf("%s
    ",t);
            t[m-1]++;
            printf("%s
    ",t);
            t[m-1]='';
            printf("%s
    ",t);
            return;
        }
        int m=0;
        while(s[n-1]=='9')n--,m++;
        int k=s[n-1]-'0';
        n--;
        if(s[n-1]%2==0){
            Div(n,2);
            printf("%s%d",t,k);
            for(int i=0;i<m;i++)printf("%d",i&1?9:0);
            printf("
    %s",t);
            for(int i=0;i<=m;i++)printf("%d",i&1?9:0);
            printf("
    %s",t);
            for(int i=0;i<m;i++)printf("%d",i&1?9:0);
            printf("
    ");
            return;
        }
        Div(n,2);
        if(t[0]!='0')printf("%s",t);
        printf("%d",k+1);
        for(int i=0;i<m;i++)printf("%d",i&1?0:9);
        printf("
    ");
        if(t[0]!='0')printf("%s",t);
        for(int i=0;i<=m;i++)printf("%d",i&1?0:9);
        printf("
    ");
        if(t[0]!='0')printf("%s",t);
        for(int i=0;i<m;i++)printf("%d",i&1?0:9);
        printf("
    ");
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)init();
    }
    View Code
  • 相关阅读:
    数据库(2019年10月30日)
    (面试题)反射(2019年10月28日)
    反射(2019年10月28日)
    常微分复习重点
    重要定理及其证明
    实变函数复习重点
    泛函分析重点定理
    自旋玻璃简介
    Fnight博文发布规范
    [分析力学]解题思路
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/14036665.html
Copyright © 2011-2022 走看看