4307 数字重构(贪心)

1. 问题描述: 

给定两个正整数 a 和 b,均不含前导 0。现在,请你对 a 进行重构,重新排列其各位数字顺序,得到一个不含前导 0 的新正整数。要求新正整数在不超过 b 的前提下,尽可能大。输出新正整数。注意,我们允许新正整数等于 a,即保持原样不变。

输入格式

第一行包含一个正整数 a。第二行包含一个正整数 b。两个输入数字均不含前导 0。

输出格式

一个不含前导 0 的正整数,表示答案。数据保证一定有解。

数据范围

前 6 个测试点满足 1 ≤ a,b ≤ 10 ^ 9。
所有测试点满足 1 ≤ a,b ≤ 10 ^ 18。

输入样例1:

123
222

输出样例1:

213

输入样例2:

3921
10000

输出样例2:

9321

输入样例3:

4940
5000

输出样例3:

4940
来源:https://www.acwing.com/problem/content/description/4310/

2. 思路分析:

对于这种题目我们首先想一下贪心能不能解决,如果不能想一下数位dp能不能够解决,可以发现这道题目是可以使用贪心来解决的。因为最终我们需要使得得到的数字字典序是最大,所以我们应该从高位到低位开始考虑,也即从大到小进行枚举,因为最终一定是有解的所以一位一位确定即可,对于当前的第i位,我们从大到小枚举0~9的所有数字,判断当前位填x是否满足要求,如何判断当前位填x是否满足要求呢?可以发现当前位之前所有的位与当前位之后的最小排列对应所有位位构成的数字需要小于等于b的时候满足要求,也即amin <= a <= b:

4307 数字重构(贪心)_第1张图片

3. 代码如下:

from typing import List



class Solution:
    # 获取当前位为x的时候构成的最小数字
    def getMin(self, x: int, count: List[int]):
        count[x] -= 1
        # 按顺序枚举即可得到最小的排列
        res = str(x)
        for i in range(10):
            for j in range(count[i]):
                res += str(i)
        # 恢复现场
        count[x] += 1
        return res

    def process(self):
        a, b = input(), input()
        count = [0] * 10
        n = len(a)
        # 统计各个数字出现的次数
        for i in range(n):
            count[int(a[i])] += 1
        if n < len(b):
            s = list(a)
            # 当a的长度小于b的长度的时候那么从大到小排序得到的就是答案
            s.sort(reverse=True)
            return "".join(s)
        # 从高位开始枚举这样得到的一定是字典序最大的
        res = ""
        for i in range(n):
            for j in range(9, -1, -1):
                if count[j] > 0 and res + self.getMin(j, count) <= b:
                    # 减1表示当前数字剩余可以使用的次数减1
                    count[j] -= 1
                    res += str(j)
                    # 当前位填x符合题目要求那么直接break
                    break
        return res


if __name__ == "__main__":
    print(Solution().process())

你可能感兴趣的:(acwing-提高,算法)