ZOJ 3816 Generalized Palindromic Number (DFS,暴力枚举)

A number that will be the same when it is written forwards or backwards is known as a palindromic number. For example, 1234321 is a palindromic number.

We call a number generalized palindromic number, if after merging all the consecutive same digits, the resulting number is a palindromic number. For example, 122111 is a generalized palindromic number. Because after merging, 122111 turns into 121 which is a palindromic number.

Now you are given a positive integer N, please find the largest generalized palindromic number less thanN.

Input

There are multiple test cases. The first line of input contains an integerT (about 5000) indicating the number of test cases. For each test case:

There is only one integer N (1 <= N <= 1018).

Output

For each test case, output the largest generalized palindromic number less thanN.

Sample Input
4
12
123
1224
1122
Sample Output
11
121
1221
1121

题意为给定一个数N,求小于N的最大“回文数”。 “回文数”要求连续相同的数可以压缩成一个数,比如1233221,可以压缩成12321,是回文数。

思路为暴力枚举,相当于填数字,首先枚举填左边第一个数字(从大到小),然后在里面枚举右边有多少个和它一样的数。注意剪枝,左边当前数字如果符合题意的话,就不用再继续枚举当前数字了,因为要求最大。
#include <iostream>
#include <string.h>
#include <string>
using namespace std;
string str;
long long N,len;
int Left[20],Right[20];
long long max(long long a,long long b)
{
    return a>b?a:b;
}
long long GetNum(int L,int R)//返回当前回文数的值
{
    long long n=0;
    for(int i=1;i<=L;i++)
        n=n*10+Left[i];
    for(int i=R;i>=1;i--)//注意存储的顺序,原数的最后一位是Right数组存储的第一位  
        n=n*10+Right[i];
    return n;
}
long long DFS(int L,int R,int cur)//L表示左边数字的个数,R表示右边数字的个数,cur表示当前数字是否是边界
//比如2511,第一位2就是边界,所求回文数的该位要<=2;其他位就不是边界,可以从9开始  
{
    long long ans=-1;
    if((L+R-1)==len)//因为DFS给左边第L位赋值时,递归参数是L+1,所以L+R-1==len才是递归出口
    {
        ans=GetNum(L-1,R);
        if(ans>=N)return -1;//我们要求的回文数的值要小于N
        else return ans;
    }
    int now=cur?str[L-1]-'0':9;//给左边第L位赋值,判断该位是否是边界
    for(int i=now;i>=0;i--)//枚举第L位的值
    {
        Left[L]=i;
    //如果(当前L是第一位或者(L不是第一位但当前位和前一位不一样))且(不能(L是第一位且值i为0))且(数字还没有填完)  
        if((L==1||(L>1&&Left[L]!=Left[L-1]))&&(!(L==1&&i==0))&&(L+R)!=len)
        {
            for(int k=1;L+R+k<=len;k++)//枚举右半部分与第L位相同的有多少位 
            {
                Right[R+k]=i;
                ans=max(ans,DFS(L+1,R+k,cur&&now==i));//为什么我们这必须判断now==i呢?因为一旦该位的值i不是now
//那么就一定小于now,所以它影响到下一位的值是否可以从9开始取值。比如原数111,若我们得出10#,那么#从9开始
            }
        }
        else
            ans=max(ans,DFS(L+1,R,cur&&now==i));
        if(ans>0)return ans;//如果当前的ans符合题意就不用往下搜索了,这是剪枝
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>str;
        N=0;
        len=str.length();
        for(int i=0;i<len;i++)
            N=N*10+(str[i]-'0');
        long long ans=DFS(1,0,1);
        cout<<ans<<endl;
    }
    return 0;
}

                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值