[Codeforces946E][贪心]Largest Beautiful Number

针对一个特殊数——美丽数的定义,即长度为偶数并可排列成回文数的整数,本文提出了一种高效算法来寻找小于给定数值n的最大美丽数。通过分析数位特征和运用贪心策略,该算法能在较短时间内得出最优解。

翻译

定义一个数为美丽数,当且仅当这个数长度为偶数,且他的某一个排列可以组成一个回文数
给出n(n<=2*10^100000),求小于n的最大美丽数

题解

从定义中可以发现,一个数为美丽数的情况,当且仅当他出现过的所有数字出现的次数均为偶数次
贪心地想,如果这个数较高的位能取大,那么一定取大
预处理在当前位置i,每个数字出现的次数是偶数次还是奇数次
枚举一个位置i,表示第i位更改,1~i-1位不变
枚举这个位置改成数j
然后可以处理出在i,现在每个数字出现的次数是奇数还是偶数次
设cnt表示在i出现次数为奇数次的数
如果len-i < cnt,表示后面没有那么多位置可以把前面需要补成偶数次的数补成偶数次,那么继续枚举
否则,i+1~len-cnt位全部填9,len-cnt+1~len位从高到低填需要补偶的数
如果找不到,那么直接输出len-2个9
特判两种情况
对于len为奇数,直接输出len-1个9
对于首位为1,末尾为0或1,其他位为0的情况,直接输出len-2个9

/*
翻译:
定义一个数为美丽数,当且仅当这个数长度为偶数,且他的某一个排列可以组成一个回文数
给出n(n<=2*10^100000),求小于n的最大美丽数 
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
char ch[210000];
int a[210000],g[15],len;
int f[210000][10];
void sol()
{
    for(int i=len;i>=1;i--)
        for(int j=a[i]-1;j>=0;j--)
        {
            if(i==1 && j==0)break;
            int cnt=0,maxx=0;
            for(int k=0;k<=9;k++)
            {
                int op=0;if(k==j)op=1;
                if(f[i-1][k]^op==1)cnt++,maxx=k;
            }
            if(len-i<cnt)continue;
            for(int k=1;k<=i-1;k++)printf("%d",a[k]);
            printf("%d",j);
            for(int k=i+1;k<=len-cnt;k++)printf("9");
            int t=maxx;
            for(int k=len-cnt+1;k<=len;k++)
            {
                int op=0;if(t==j)op=1;else op=0;
                while(f[i-1][t]^op!=1){t--;if(t==j)op=1;else op=0;}
                printf("%d",t);t--;
            }
            printf("\n");
            return ;
        }
    for(int i=1;i<len-2;i++)printf("9");
    printf("\n");
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(g,0,sizeof(g));
        scanf("%s",ch+1);len=strlen(ch+1);
        for(int i=1;i<=len;i++)a[i]=ch[i]-'0',g[a[i]]++;
        if(len%2==1)
        {
            for(int i=1;i<len;i++)printf("9");
            printf("\n");continue;
        }
        if((g[1]==1 && g[0]==len-1 && a[1]==1) || (g[1]==2 && g[0]==len-2 && a[1]==1 && a[len]==1))
        {
            for(int i=1;i<len-1;i++)printf("9");
            printf("\n");continue;
        }
        for(int i=0;i<=9;i++)f[0][i]=0;//0 偶数次
        for(int i=1;i<=len;i++)
            for(int j=0;j<=9;j++)
            {
                if(a[i]==j)f[i][j]=f[i-1][j]^1;
                else f[i][j]=f[i-1][j];
            }
        sol();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值