ZOJ 1136 Multiple( 搜索 BFS )

题目: 给一个数N,N>= 0, N < 5000, 然后给出m个数,求N的最小倍数,这个倍数比一定是有这m个数组成,比如N=12, m个数里面有1,2, 那么12是N的最小倍数,是有m个数字中的1和2合成的。注意,m中的每个数字是可以重复使用的。

解析:

第一,分析可知,由于它是加的位数,而不是数字,所以,对于数一定要有处理,不可能盲目的追加位数!

第二,这m个数,根据题意,可以组成的数字是无穷多的

所以,这里需要一个解决的办法,那就是取余。事实上,任何一个数对n取余,一共只能有n个结果(如果算0的话)。而且有这样的一个式子,如果x%n == y%n, 那么x%n * 10 + c == y%n *10 + c,由此可见,我们只要判断这个余数即可,将所得的余数和剩下的m个数组和,如果得到的数对n取余的余数之前组和过,那么就不需要再进行判断了,因为,之前已经判断过了。也就是说,这里和m个数是谁不重要,重要的是,这个余数,一旦这m个数组和成的数。

接下来说一下大致的算法:

首先将m个数从下到大排序,这样在bfs的过程中也是从小到大遍历,保证结果最小。

然后,开始bfs,建立一个vis数组,来记录n的那些已经遍历过的余数,队列里面存储的也是余数,然后从小到大遍历。比较重要的是记得建立一个父节点数组,用来输出。

最后要注意的是,对于父节点数组,要指向的节点是自己新建立的,而不是那m个数,这个很容易想到。

解释一下代码:结构体NODE里面,存储这个点的id,余数,父节点,和对应的数。bfs判断是否存在一个这个倍数,outprintf递归输出。bfs中,第一个for循环, 算是用来初始化的,有一点要注意的是,0不能加入到队列里面,因为求的是最小正整数。然后后面的那个while就正常做了。

代码:

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

const int N = 6000;
const int M = 100001;
int n, m, end, cnt, num[M], wnum[M];
bool vis[N];
struct NODE {
    int idn, x, p, id;
}e[N];
int solve ( int x ) {
    if ( x == 0 ) return 1;
    int ans = 0;
    while ( x > 0 ) {
        ans += 1;
        x /= 10;
    }
    return ans;
}
int fun( int x ) {
    int ans = 1;
    for ( int i = 0; i < x; ++i ) ans *= 10;
    return ans;
}
bool cmp ( int a, int b ) { return a < b; }
bool bfs() {
    queue <NODE> Q;
    for ( int i = 0, x; i < m; ++i ) {
        x = num[i] % n;
        if ( !vis[x] && num[i] ) {
            vis[x] = true;
            e[cnt].x = x, e[cnt].idn = num[i], e[cnt].p = -1, e[cnt].id = cnt;
            Q.push(e[cnt]);
            if ( x == 0 ) {
                end = cnt;
                return true;
            }
            cnt++;
        }
    }
    while ( !Q.empty() ) {
        NODE u = Q.front(); Q.pop();
        for ( int i = 0; i < m; ++i ) {
            int x = (u.x * fun( wnum[i] ) + num[i]) % n;
     //printf("%d\n", x);     
            if ( !vis[x] ) {
                vis[x] = true;
                e[cnt].x = x, e[cnt].p = u.id, e[cnt].idn = num[i], e[cnt].id = cnt;
                if ( x == 0 ) {
                    end = cnt;
                    return true;
                }
                Q.push( e[cnt] );
                cnt++;
            }
        }
    }
    return false;
}

void outprintf( int x ) {
    if ( x == -1 ) return;
    outprintf(e[x].p);
    printf("%d", e[x].idn);
}
int main() 
{
    while ( scanf("%d%d", &n, &m) == 2 ) {
        cnt = 0;
        for ( int i = 0; i < m; ++i ) scanf("%d", &num[i]);
        if ( n == 0 ) {
            printf("0\n"); 
            continue;
        }
        sort( num, num + m, cmp );
        for ( int i = 0; i < m; ++i ) wnum[i] = solve( num[i] );
        memset(vis, 0, sizeof(vis));
        if ( bfs() ) outprintf( end );
        else printf("0");
        printf("\n");
    }
}



  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值