• UPC-6616 Small Multiple(BFS广搜&双向队列)


    题目描述
    Find the smallest possible sum of the digits in the decimal notation of a positive multiple of K.
    Constraints
    2≤K≤105
    K is an integer.

    输入
    Input is given from Standard Input in the following format:
    K

    输出
    Print the smallest possible sum of the digits in the decimal notation of a positive multiple of K.

    样例输入
    6

    样例输出
    3

    提示
    12=6×2 yields the smallest sum.

    题意:给出一个数k,找到一个k的正整数倍数(k*i)使得这个倍数的每一数位上的值加和最小。如6的2倍是12,1+2=3,是所有倍数中各位加和最小的。

    思路:通过广搜实现,首先这题一开始想的针对数字和倍数找数学规律的方法是不可取的,观察了各种数的倍数特征后发现并没有什么规律,并且因为k值很大,因此不能通过直接遍历i然后check最小按位加和的方法来筛选数字。

    那么通过不一样的想法,因为是按位加和,因此我们应该从最小的按位加开始查找,如1,10,100,1000…..然后是11,1100,11000,101,1010,1011,2,20,220,21,可以发现,我们对于按位数字加和会使数字出现,某一位上值+1,或整体数字*10的操作,一个数字乘10,按位加和不会改变,一个数字+1,按位加和改变,就能一个不漏的按序找到一些加和较小的值。

    接下来是判断该数是否是k的倍数,在搜索的过程中得到了一个新的数,这个数直接对k取模,如果为0即k的倍数,那么这个答案就是符合条件的。但是要注意,因为广搜的过程中一个数会出现两个分支的搜索路径,一个+1,一个*10,很明显如果我们要求加和最小就应该先遍历*10的结果是否符合条件,因为这是不改变加和的状态,在此基础上,一个数完成了所有*10的搜索,才能遍历下一个+1的搜索。这就需要双向队列对搜索进行分类,右端插入+1分支,左端插入*10分支,一直从*10端取数判断,并且遍历过程中标记数是否检查过,因为结果一直对k取模,所以遍历的数不会超过k值大小。

    其中7的答案你以为是3吗?7 * 3=21,1+2=3?太天真了!其实是2,因为7 * 143=1001

    还有一个问题是,你是否认为,我虽然说的操作是对每个数按位*10和+1,但投入到队列中搜索的数其实是对k取模过的,因此并不准确,如我想搜索的是21,那么当2*10的时候塞入队列的其实是20%7=6,这样一来下次想要遍历20+1的 时候其实遍历到的是6+1=7,连按位加和的2都与6对不上了,但其实这个取模操作是能保证运算正确的,如10%7=3,投入3之后,下次想要遍历100,那么取出3,然后3*10=30,再取模7,得到30%7=2,这与100%7=2的结果是一样的,不会造成影响。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+7;
    bool vis[maxn];
    struct node
    {
        int num,sum;
        node() {}
        node(int a,int b)
        {
            num=a;
            sum=b;
        }
    };
    deque<node>q;///双向队列保证搜索顺序,因为要先遍历了再同一个按位加和的情况下,所有乘10的情况,再去计算下一个按位加和是否是倍数
    int k;
    int main()
    {
        scanf("%d",&k);
        memset(vis,false,sizeof vis);
        q.clear();
        q.push_back(node(1,1));
        while(!q.empty())
        {
            node tmp=q.front();///前端表示尽量保持按位加的变化
            q.pop_front();
            if(!vis[tmp.num])
            {
                vis[tmp.num]=true;///标记已经遍历过的数
                if(!tmp.num)///当对k的取模已经为0且第一个搜到的,表示按照最小按位加且是k的倍数
                {
                    printf("%d
    ",tmp.sum);
                    break;
                }
                q.push_back(node((tmp.num+1)%k,tmp.sum+1));///后端表示,当前端都被标记过后,从后端取出+1的按位和来继续搜索所有*10的情况
                q.push_front(node((tmp.num*10)%k,tmp.sum));
            }
        }
    }
    
  • 相关阅读:
    java对象的四种引用
    linux安装python3
    ORACLE配置重做日志文件
    oracle添加控制文件,ORA-00214: 错误
    oracle new 和old 关键字
    with open
    json库
    requests
    urllib模块
    python读取txt天气数据并使用matplotlib模块绘图
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135730.html
Copyright © 2020-2023  润新知