UVa 1189 Find the Multiple

题目描述

给定一个正整数 nnn,编写程序找到一个非零的倍数 mmm,使得 mmm 的十进制表示仅包含数字 000111。题目保证 nnn 不超过 200200200,并且存在一个对应的 mmm,其十进制表示不超过 100100100 位。

输入格式
输入文件可能包含多个测试用例。每行包含一个 nnn 的值(1≤n≤2001 \leq n \leq 2001n200)。以一行包含 000 结束输入。

输出格式
对于每个 nnn,输出一行对应的 mmmmmm 的十进制表示不能超过 100100100 位。如果对于给定的 nnn 有多个解,输出任意一个均可。

样例输入

2
6
19
0

样例输出

10
100100100100100100
111111111111111111

题目分析

问题理解

我们需要找到一个数 mmm,满足:

  1. mmmnnn 的倍数(m≠0m \neq 0m=0
  2. mmm 的十进制表示中只包含字符 01
  3. mmm 的位数不超过 100100100

直接枚举的困难

最直接的想法是枚举所有由 01 组成的数字,从小到大检查是否能被 nnn 整除。但是,可能的数字数量是 21002^{100}2100 级别,这显然不可行。

关键思路:模运算 + BFS\texttt{BFS}BFS

我们可以利用模运算来避免处理大数,同时使用广度优先搜索(BFS\texttt{BFS}BFS)来按长度递增的顺序搜索解。

核心观察

  • 我们只关心数字模 nnn 的余数
  • 如果两个数字模 nnn 的余数相同,那么它们后续添加相同的数字(01)后,余数的变化规律相同
  • 因此,对于每个余数,我们只需要保留第一次达到该余数的数字(这保证我们找到的是最小的解)

BFS\texttt{BFS}BFS 状态设计

  • 状态:当前数字模 nnn 的余数
  • 初始状态:余数为 111(对应数字 1
  • 状态转移
    • 在当前数字末尾加 0:新余数 = (当前余数×10) mod n(当前余数 \times 10) \bmod n(当前余数×10)modn
    • 在当前数字末尾加 1:新余数 = (当前余数×10+1) mod n(当前余数 \times 10 + 1) \bmod n(当前余数×10+1)modn
  • 终止条件:找到余数为 000 的状态

算法正确性

  • 由于余数范围是 000n−1n-1n1,最多有 nnn 个不同状态,BFS\texttt{BFS}BFS 会在有限步内结束
  • 题目保证存在不超过 100100100 位的解,因此不会无限循环
  • BFS\texttt{BFS}BFS 按长度递增搜索,保证找到的是最小的解

复杂度分析

  • 时间复杂度O(n)O(n)O(n),每个余数最多访问一次
  • 空间复杂度O(n)O(n)O(n),用于存储访问标记和队列

代码实现

// Find The Multiple
// UVa ID: 1189
// Verdict: Accepted
// Submission Date: 2025-10-16
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <iostream>
#include <queue>
#include <string>
#include <vector>
using namespace std;

string findMultiple(int n) {
    // 特殊情况:n=1 时直接返回 "1"
    if (n == 1) return "1";
    
    // BFS 队列,存储 (余数, 对应的数字字符串)
    queue<pair<int, string>> q;
    // 访问标记数组,记录每个余数是否已被访问
    vector<bool> visited(n, false);
    
    // 从数字 1 开始搜索
    q.push({1 % n, "1"});
    visited[1 % n] = true;
    
    while (!q.empty()) {
        // 取出队首元素
        int rem = q.front().first;
        string numStr = q.front().second;
        q.pop();
        
        // 如果余数为 0,找到解
        if (rem == 0) {
            return numStr;
        }
        
        // 尝试在当前数字末尾加 0
        int newRem = (rem * 10) % n;
        if (!visited[newRem]) {
            visited[newRem] = true;
            q.push({newRem, numStr + "0"});
        }
        
        // 尝试在当前数字末尾加 1
        newRem = (rem * 10 + 1) % n;
        if (!visited[newRem]) {
            visited[newRem] = true;
            q.push({newRem, numStr + "1"});
        }
    }
    
    return ""; // 理论上不会执行到这里
}

int main() {
    int n;
    // 读取输入直到遇到 0
    while (cin >> n && n != 0) {
        cout << findMultiple(n) << endl;
    }
    return 0;
}

总结

本题通过将大数问题转化为模运算问题,巧妙地避免了直接处理大数的复杂性。BFS\texttt{BFS}BFS 按长度递增搜索保证了解的最优性,而模运算的状态压缩确保了算法的高效性。这种"余数状态 + BFS\texttt{BFS}BFS"的思路在处理只包含特定数字的倍数问题时非常有效。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值