codeforces 915 C Permute Digits

Description
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   10 18 ). The second line contains integer b (1   b   10 18 ). 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.

Examples

InputOutput
123
222
213
InputOutput
3921
10000
9321
InputOutput
4940
5000
4940

题意:输入两个数 a b,重新排列 a 的各个位使其成为一个不比b大的最大的数,并输出这个数作为答案。

思路:找到不大于 b 的最大的数,根据数的性质,理想的数最好每一位都应该接近b,而且应该优先让高位接近。

解法:

  • 动态规划解决,记录a中0~9出现的次数
    • 从最高位(i=0)开始,并假设能找到和b一样的数
    • 如果在a中有至少一个b[i],就把b[i]填到答案的第i位,并把a中b[i]的次数-1
    • 如果在a中没有b[i],或者在此位填b[i]不合理,说明假设不成立,在a中继续找一个最接近b[i]-1的数填到答案中
      • 如果能找到,说明之前的填法是正确而且最大的,那么把a中没用到的数按从大到小依次填到答案中,所得到的数就是最佳的答案
      • 如果不能找到,说明之前的填法有问题,之前的有一位只能接近不能等于,那么返回到上一位
    • 如果在此位填b[i]合理,继续下一位(i++)
  • 如果b的位数比a多,则a无论怎么排列都一定小于b,直接把a按从大到小排列所得的最大数就是答案
#include <stdio.h>
#include <string.h>

int na, nb;
char a[20];
char b[20];

int cnta[10];
int numb[20];
char ans[20];
//int cntans[10];
#define c2n(ch) (ch-'0')
#define n2a(num) (num+'0')

bool solve(int t) {
    if (t == na)return true;//填好了
    //处理答案的第t位
    if (cnta[numb[t]] > 0) {//如果a中还有b[t],就尝试填入
        --cnta[numb[t]];
        ans[t] = b[t];
        //解释:solve(t+1)处理下一位,如果填好了就返回true,不能这样填就返回false

        if (solve(t + 1) == false) {//当前位填b[i]不合适,必须填更小的数进去
            ++cnta[numb[t]];//把之前填入的numb[t]取出来
            int lt = numb[t] - 1;//lt 取 less than之意
            while (cnta[lt] <= 0 && lt >= 0)--lt;//寻找合适的比b[i]小的数
            if (lt < 0)return false;//没有找到
            else {
            //找到了,把lt填到答案
            --cnta[lt];
            ans[t] = lt + '0';
            //从这里开始已经有一个高位比b小了,只需要把剩下的位按最大把b填满就行
            int gt = 9;
            for (int asi = t+1; asi < na; ++asi) {
                while (cnta[gt] <= 0 && gt >= 0)--gt;
                    ans[asi] = gt + '0';
                    --cnta[gt];
                }
                return true;//题意保证一定有答案
            }
        }

        //如果填的数是合适的就不做任何操作
        return true;
    }
    else {如果a中没有有b[t],就找一个小的数填入
        int lt = numb[t];
        while (cnta[lt] <= 0 && lt >= 0)--lt;
        if (lt < 0)return false;//没有找到
        else {
            //找到了,把lt填到答案
            --cnta[lt];
            ans[t] = lt + '0';
            //从这里开始已经有一个高位比b小了,只需要把剩下的位按最大把b填满就行
            int gt = 9;
            for (int asi = t+1; asi < na; ++asi) {
                while (cnta[gt] <= 0 && gt >= 0)--gt;
                ans[asi] = gt + '0';
                --cnta[gt];
            }
            return true;
        }

    }
}


int main() {
    for (na = 0;;na++) {
        scanf("%c", &a[na]); 
        if (a[na] == '\n')break;
        cnta[c2n(a[na])]++;
    }
    for (nb = 0;; nb++) {
        scanf("%c", &b[nb]);
        if (b[nb] == '\n')break;
        numb[nb] = c2n(b[nb]);
    }
    if (nb > na) {
        for (int t = 9;t>=0;--t) {
            while (cnta[t]-- > 0) {
                printf("%c", n2a(t));
            }
        }
    }
    else {
        solve(0);
        for (int i = 0; i < na; i++) {
            printf("%c", ans[i]);
        }
    }
}

趁着刚ac把思路整理到博客,感觉有点杂乱,说是动态规划其实一点也不像,想清楚了再优化下吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值