ZOJ 1136&&POJ 1465 Multiple(BFS+同余定理)

21 篇文章 0 订阅
20 篇文章 0 订阅
Multiple
Time Limit: 1000MS Memory Limit: 32768K
Total Submissions: 7215 Accepted: 1546

Description

a program that, given a natural number N between 0 and 4999 (inclusively), and M distinct decimal digits X1,X2..XM (at least one), finds the smallest strictly positive multiple of N that has no other digits besides X1,X2..XM (if such a multiple exists).

Input

The input has several data sets separated by an empty line, each data set having the following format:

On the first line - the number N
On the second line - the number M
On the following M lines - the digits X1,X2..XM.

Output

For each data set, the program should write to standard output on a single line the multiple, if such a multiple exists, and 0 otherwise.

An example of input and output:

Sample Input

22
3
7
0
1

2
1
1

Sample Output

110
0

Source


题目大意:

    输入一个数字N,给出一些0~9的数字,求每一位都有这些数字组成的最小的N的倍数的正整数,如果不存在输出0。


解题思路:

    BFS搜索,我们先把这些给出的数字排序,这样就可以保证先搜索的是最小的。然而如果直接这样搜索,绝对会TLE,因此我们需要使用同余定理进行剪枝。

    由同余定理可知:对于A,B两个数,如果A%N==C&&B%N==C,即A==a*N+C&&B=b*N+C,如果我们在后面再加一个数X,则A*10+X==a*N*10+C*10+X、B*10+X==b*N*10+C*10+X,所以对于任意膜N相当两个数,再它们之后再加一个数字形成的新数字膜N一定相等。又应为题目要我们输出最小的值,对于后面出现的膜相等的数一定比第一个大,所以就没有必要对它们进行搜索。利用这一点就可以非常有效的进行剪枝。

    还有一点,由于这个数字可以非常大,会远远超过long long,我们要在bfs队列中每一次保存当前位数,最后用类似链表的方法输出(用大数不知道会不会超时,我没有试)。

    最后还需要特别注意一下当N等于0的时候需要特判,否则会RE。


AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

struct Point
{
    int last,left,now;//上一个数字的下标,余数,当前数字
    Point(int la=0,int le=0,int n=0):last(la),left(le),now(n){}
};

const int maxn=4999+5;
const int maxm=10+5;
int N,M,digit[maxm];
Point que[maxn*400];//用数组模拟队列
bool vis[maxn];

int bfs()
{
    memset(vis,0,sizeof vis);
    int s=0,t=0;
    for(int i=0;i<M;++i)
    {
        if(digit[i]==0)
            continue;
        que[t++]=Point(-1,digit[i]%N,digit[i]);
        if(digit[i]%N==0)
            return i;
        vis[digit[i]%N]=true;
    }
    while(t>=s)//队列为空,说明不存在这样的数。
    {
        for(int i=0;i<M;++i)//枚举下一位
        {
            Point p=que[s];
            p.last=s;
            p.left=(p.left*10+digit[i])%N;
            p.now=digit[i];
            if(vis[p.left])//余数已经出现过,没必要继续搜索
                continue;
            vis[p.left]=true;
            que[t++]=p;
            if(!p.left)
                return t-1;
        }
        ++s;
    }
    putchar('0');
    return -1;
}

void out(int x)//输出
{
    if(x==-1)
        return ;
    out(que[x].last);
    putchar('0'+que[x].now);
}

int main()
{
    while(~scanf("%d%d",&N,&M))
    {
        for(int i=0;i<M;++i)
            scanf("%d",&digit[i]);
        sort(digit,digit+M);
        if(N==0)//N==0特判
            puts("0");
        else
        {
            out(bfs());
            putchar('\n');
        }
    }
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值