1/26 训练五 深度优先搜索(dfs)E .(dfs 减枝! 注意回溯!给两个数ab,输出不超过b的最大a的重排) Permute Digits

1/26 训练五 深度优先搜索(dfs)

E .(dfs 减枝! 注意回溯!给两个数ab,输出不超过b的最大a的重排) Permute Digits

You are given two positive integer numbers a and b. Permute (change order) of the digits of a to construct maximal number not exceeding b. No number in input and/or output can start with the digit 0.

It is allowed to leave a as it is.

Input

The first line contains integer a (1 ≤ a ≤ 1018). The second line contains integer b (1 ≤ b ≤ 1018). Numbers don't have leading zeroes. It is guaranteed that answer exists.

Output

Print the maximum possible number that is a permutation of digits of a and is not greater than b. The answer can't have any leading zeroes. It is guaranteed that the answer exists.

The number in the output should have exactly the same length as number a. It should be a permutation of digits of a.

Example

Input

123
222

Output

213

Input

3921
10000

Output

9321

Input

4940
5000

Output

4940

题意:
给出两个数a,b,a的数字位置可以改变,要求输出不超过b的最大的a的重排数。
题目保证有解,且ab无前导0.
ab是大数!


思路:
1、对于大数,我们一般用字符串处理。
2、在这里如果a的长度小于b,那么直接将a按照字典序从大到小输出即可。
3、如果ab一样长,那么就应该将ab每一位上的数字进行比较。
对于每一位 i 上的数字的摆放,我们都想把a里面最大且未被使用过的数字放在这个位置上。
过程如下:
# 1、如果这个数字大于B[i], 显然不可以。
# 2、如果这个数字恰好等于B[i],那么就将这个数字摆放在第i位,并将它标记为vis, 然后进行下一位 i+1上的数字的 摆放。
# 3、如果这个数字小于B[i],那么就将这个数字摆放在第i位。此时第i +1位及其后面的位数无论怎么放都不会超过b了,所以我们可以直接按照字典序将没有被使用的数字输出即可。

↑↑↑看似思路很清晰,但是会有bug。

以下为WA代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#define MAXN 20

using namespace std;

int la, lb, vis[MAXN];
char a[MAXN], b[MAXN];

bool cmp(char a, char b)
{
    return a > b;
}

void play(int x)
{
    for(int i = 0; i < la; i++)
    {
        if(!vis[i] && a[i] == b[x])//如果找到相等的数字
        {
            printf("%c", a[i]);//输出该数字
            vis[i] = 1;//标记
            play(x + 1);//放置下一位
            return ;//放置完毕,返回
        }
        else if(!vis[i] && a[i] < b[x])//如果找到小于的数字
        {
            printf("%c", a[i]);//输出
            vis[i] = 1;
            for(int j = 0; j < la; j++)//将没有使用的数字按照字典序输出
                if(!vis[j])
                    printf("%c", a[j]);
            return ;
        }
    }
}

int main()
{
    scanf("%s%s", a, b);
    memset(vis, 0, sizeof(vis));
    la = strlen(a);
    lb = strlen(b);
    sort(a, a + la, cmp);
    if(la < lb)
        puts(a);
    else
    {
        play(0);
        printf("\n");
    }
    return 0;
}

错误:
没有进行回溯,死在了test 6上面。我开始想,既然题目说保证有解,那么就一定可以按照上述步骤解决,不会有返回的情况。但是 如 1230 1300 , 这一组样例,如果按照上述思路,那么我应该在第二位放置3,可是放置了3,再放置0,变成130的时候,2没地方可以放了,那么就会输出123 。这里说明上述思路是存在漏洞的。还是需要回溯

那么如何知道要回溯了呢?我又加了一个标记,flag。
WA代码如下:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#define MAXN 20

using namespace std;

int la, lb, vis[MAXN], flag;
char a[MAXN], b[MAXN];

bool cmp(char a, char b)
{
    return a > b;
}

void play(int x)
{
    for(int i = 0; i < la; i++)
    {
        if(!vis[i] && a[i] == b[x])
        {
            printf("%c", a[i]);
            vis[i] = 1;
            play(x + 1);
            if(!flag)//如果上面的play(x+1)执行成功,那么错误标志flag仍为0,说明所有数字放置成功,返回即可
                return ;
            else//否则说明这个数字不应该放在这里,取消它,将VIS置0
            {
                vis[i] = 0;
                flag = 0;//要将之前的错误标志置0,因为我已经返回,将这个错误避免了。
            }
        }
        else if(!vis[i] && a[i] < b[x])
        {
            printf("%c", a[i]);
            vis[i] = 1;
            for(int j = 0; j < la; j++)
                if(!vis[j])
                    printf("%c", a[j]);
            return ;
        }
    }
    flag = 1;//如果上述过程没有执行到return,说明不能成功的放置所有数字,标记该放置方法错误。
}

int main()
{
    scanf("%s%s", a, b);
    memset(vis, 0, sizeof(vis));
    la = strlen(a);
    lb = strlen(b);
    flag = 0;
    sort(a, a + la, cmp);
    if(la < lb)
        puts(a);
    else
    {
        play(0);
        printf("\n");
    }
    return 0;
}

错误:
虽然我用了一个flag作为回溯标志,但是由于我每次尝试放置数字的时候,都会先将他输出printf(“%c”, a[i]); ,所以就算我发现执行不成功,错误标志flag=1,将vis置0,依然不能撤回输出操作,上面那个例子,1230 1300 ,结果会输出130230,因为先尝试了13 0发现无法进行下去,于是又输出230。 因此,我要想办法将输出操作可以撤回,于是我想到用一个数组来存放答案,如果发现操作进行不下去,那么我既要将vis置0,也要将放入的答案拿出来。这里不需要讲答案数组的下标进行处理,因为如果这个操作进行不下去,循环将接着进行,但是放置答案的下标依旧没变,因为这个函数还是在进行放置第i位的操作,所以ans答案数组还是会将下一次尝试结果放在第i位。

WA代码如下:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#define MAXN 20

using namespace std;

int la, lb, vis[MAXN], flag;
char a[MAXN], b[MAXN], ans[MAXN];

bool cmp(char a, char b)
{
    return a > b;
}

void play(int x)
{
    if(flag)
        return ;
    if(x == la)
    {
        flag = 1;
        return ;
    }
    for(int i = 0; i < la; i++)
    {
        if(!vis[i] && a[i] == b[x])
        {
            ans[x] = a[i];
            vis[i] = 1;
            play(x + 1);
            if(flag)
                return ;
            else
                vis[i] = 0;
        }
        else if(!vis[i] && a[i] < b[x])
        {
            ans[x] = a[i];
            vis[i] = 1;
            int d = x + 1;
            for(int j = 0; j < la; j++)
                if(!vis[j])
                    ans[d++] = a[j];
            flag = 1;
            return ;
        }
    }
}

int main()
{
    scanf("%s%s", a, b);
    memset(vis, 0, sizeof(vis));
    la = strlen(a);
    lb = strlen(b);
    flag = 0;
    sort(a, a + la, cmp);
    if(la < lb)
        puts(a);
    else
    {
        play(0);
        for(int i = 0; i < la; i++)
            printf("%c", ans[i]);
        printf("\n");
    }
    return 0;
}

错误:
虽然我将答案放置在ans中能输出正确答案了,但是由于操作次数过多,每一位都要一次次尝试,一个个数字的排除,所以在test 67 TLE 了。
那么怎么优化呢? 这里要进行剪枝。由于每一次都要从第一个数字到最后一个数字遍历,看他是否被使用,与B【i】的大小关系如何。这就要进行多次循环。很浪费时间。而数字只有0~9,我直接从9开始遍历到0即可。在比较过程中,如果这个数字大于B【i】,那么原来a数组里面和这个数字一样的数字就被剪掉了,下次就会直接从比这个数字小的开始循环。如果按照之前的循环方法,按照a数组中数字出现的顺序遍历,每一次出现比B【i】大或者相等的数组,我都需要进行判断,特别是那些和B【i】相等但是又不满足条件的数,我还要再花重复的时间去函数递归。
因此我们在读取字符a的时候做预处理,将a中0~9数字出现的个数保存起来,每用一次都减1,减到0就表示没有了,就不可以再用这个数字了。遍历时,按照数字9~0依次遍历。其他思路和上面一样。
PS:要注意这里遍历用的是数字int型,而比较时,用的是字符型数字‘0’ ‘9’,要记得转换!

AC代码如下:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#define MAXN 20

using namespace std;

int la, lb, vis[MAXN], flag;
char a[MAXN], b[MAXN], ans[MAXN];

bool cmp(char a, char b)
{
    return a > b;
}

void play(int x)
{
    if(flag)
        return ;
    if(x == la)
    {
        flag = 1;
        return ;
    }
    for(int i = 9; i >= 0; i--)
    {
        if(vis[i] > 0 && i + '0' == b[x])
        {
            ans[x] = i + '0';
            vis[i]--;
            play(x + 1);
            if(flag)
                return ;
            else
                vis[i]++;
        }
        else if(vis[i] > 0 && i + '0' < b[x])
        {
            ans[x] = i + '0';
            vis[i]--;
            int d = x + 1;
            for(int j = 9; j>= 0; j--)
                while(vis[j] > 0)
                {
                    ans[d++] = j + '0';
                    vis[j]--;
                }
            flag = 1;
            return ;
        }
    }
}

int main()
{
    scanf("%s%s", a, b);
    memset(vis, 0, sizeof(vis));
    la = strlen(a);
    lb = strlen(b);
    flag = 0;
    sort(a, a + la, cmp);
    for(int i = 0; i < la; i++)
    {
        vis[a[i] - '0']++;
    }
    if(la < lb)
        puts(a);
    else
    {
        play(0);
        for(int i = 0; i < la; i++)
            printf("%c", ans[i]);
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/floraqiu/article/details/79176560