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\n",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));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值