zoj 3816 Generalized Palindromic Number(枚举加构造)

这是网络赛的H题,当时看到题的第一个想法就是构造,但是由于各种各样的问题加上构造的代码又太繁琐了,所以当时放下了这个题,没有做出来

后来看题解,网上大部分都是用搜索过的,是用的暴搜+剪枝?还是什么高效的搜索。。。反正我没看懂Orz,后来又往搜索的地方想了一下发现还是没办法控制

时间到一个能接受的范围。。。不确定性太高了,然后就接着自己的构造思想开始写,今天大概写了1个小时。。但是调试用掉了2个小时左右。。没办法。。我太弱了。。。


思路:

枚举答案与输入的数字前面有多少位相同,如果数有10位,那么我们只需要枚举有9~1相同就可以了,因为如果9~1都没构造出一个可行解答案肯定就是9个9了啊

枚举了前面有多少位相同之后把前面的数(包括不相同的第一位)和并成一个数列,然后再枚举这个数列的哪一位是对称的中心位,然后再根据中心位开始填后面的数字,然后找到一个最大的解输出就可以了。

比如输入一个数字

111222334567888   答案应该是111222334554321

现在来说一下构造过程

其实我们处理的是

111222334567887

当我们枚举到答案和数字前10位都相同时:

然后枚举第11位为d[10]-1~0

当枚举到第11位为5时

把前11位合并成一个数列

12345

然后开始枚举对称中心

当枚举到对称中心是5时直接开始填数

因为5比4大所以一直填5直到后面剩余的没填的数的个数等于4时把4321填到最后

如果合并了之后为

12354

4小于5所以先填一个5

然后比较3和5的大小发现5比3大就一直填5直到剩下的个数等于3时把321填到最后

当然还要很多种情况需要考虑比如对称中心不在12345中时等情况

这种情况就直接在末尾填54321然后如果还有没填的空就全部填9

等等。。。其实只要想到先枚举有几位相同然后再枚举对称中心在哪,后面的构造就很容易想到了,复杂度低于18的3次方~~~~




上一个我的巨挫的代码仅供参考。。其实比赛的时候还是应该直接搜的。。。。。

#include<iostream>
#include<sstream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<time.h>
#include<set>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
#define inf 0x7fffffff
#define lc l,m,index<<1
#define rc m+1,r,index<<1|1
#define max_n 100005
#define mod 1000000007
#define FOR(i,s,t) for(int i=(s);i<=(t);++i)
#define LL long long
int d[20];
int l;
bool check(LL b)
{
    l=0;
    while(b)
    {
        d[l++]=b%10;
        b/=10;
    }
    int d2[20];
    int l1=0;
    for(int i=0;i<l;i++)
    {
        if(!l1 || d[i]!=d[i-1])
        d2[l1++]=d[i];
    }
    int L=0,R=l1-1;
    while(L<=R)
    {
        if(d2[L]!=d2[R]) return 0;
        L++;
        R--;
    }
    return 1;
}
LL solve(LL n)
{
    n--;
    if(n<10) return n;
    LL b=n;
    if(check(b)) return n;
    LL res=0;
    for(int i=0;i<l;i++)//枚举从哪个数开始不同
    {
        int a=check(b);
        int d2[20];
        int l2=0;
        LL mind=-1;
		int la=d[i]-1;
        for(int j=la;j>=0;j--)
        {
			l2=0;
			d[i]=j;
            LL ans=0;
			for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];
			if(check(ans))
			{
				if(mind<ans)
					mind=ans;
			}
            for(int k=i;k<l;k++)//右侧的数
            {
                if(!l2) d2[l2++]=j;
                else if(d2[l2-1]!=d[k]) d2[l2++]=d[k];
            }

            for(int id=0;id<=(l2-1)/2;id++)//枚举对称值不包含没有对称情况
            {
                int L=id,R=id;
                bool flag=1;
                while(L>=0 && R<l2)
                {
                    if(d2[L]!=d2[R])
                    {
                        flag=0;
                        break;
                    }
                    L--;
                    R++;
                }
                if(!flag) continue;
                if(l2-R>i) continue;
               
				if(R==l2)
				{
					for(int k=0;k<i;k++)
						d[k]=d2[0];

					ans=0;
               
                    for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];

					if(ans>mind) mind=ans;
					//continue;
				}
				if(R==l2-1)
				{
					if(d2[l2-1]<d2[l2-2])
					{
						L=i-1;
						while(L>0)
						{
							d[L]=d2[l2-2];
							L--;
						}
						d[0]=d2[l2-1];
					}
					else
					{
						for(int k=0;k<i;k++)
						d[k]=d2[0];
					}
					ans=0;
               
                    for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];

					if(ans>mind) mind=ans;
					//continue;
				}
				L=i-1;
				int r=R;
				R--;
				while(L>=0 && R<l2)
                {
                    if(d2[R]>d2[R+1] || R==l2)
                    {
                        while(L>=l2-R-1)
                        {
                            d[L]=d2[R];
                            L--;
                        }
                        R++;
                        while(L>=0)
                        {
                            d[L]=d2[R];
                            L--;
                            R++;
                        }
                    }
                    else
                        d[L--]=d2[R++];
                }
                ans=0;
               
                    for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];

                if(ans>mind) mind=ans;



				R=r;
				L=i-1;
				//R--;
                while(L>=0 && R<l2)
                {
                    if(d2[R]>d2[R+1] || R+1==l2)
                    {
                        while(L>=l2-R-1)
                        {
                            d[L]=d2[R];
                            L--;
                        }
                        R++;
                        while(L>=0)
                        {
                            d[L]=d2[R];
                            L--;
                            R++;
                        }
                    }
                    else
                        d[L--]=d2[R++];
                }
                ans=0;
               
                    for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];

                if(ans>mind) mind=ans;

            }
            if(i<l2) continue;
            //不含对称值的情况
            for(int k=0;k<i;k++)
            {
                if(l2>0)
                d[k]=d2[l2-1];
                else
                d[k]=9;
                l2--;
            }
            ans=0;
            for(int k=l-1;k>=0;k--)
                    ans=ans*10+d[k];
            if(ans>mind) mind=ans;

        }
        if(mind>0) return mind;

    }
	check(b);
    for(int i=0;i<l-1;i++)
    res=res*10+9;
    return res;
}
//LL a[10000000];
int main()
{
    LL T;
	//freopen("C:\\Users\\admin\\Desktop\\out.txt","r",stdin);
	//freopen("C:\\Users\\admin\\Desktop\\output.txt","w",stdout);
     LL i,n;
	scanf("%lld",&T);
	 i=1;
	/* while(~scanf("%lld",&a[i]))
	 {
		// printf("%lld\n",a[i]);
		 i++;
	 }*/
	
	// for(n=1;n<=1000000;n++)
	 while(T--)
	{
		scanf("%lld",&n);
		printf("%lld\n",solve(n));
		/*if(a[n]!=solve(n))
		{
			printf("%lld %lld %lld\n",n,a[n],solve(n));
			//system("pause");
		}*/
	 }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值