HDU 4474(Yet Another Multiple Problem)(BFS+同余定理)

来源:互联网 发布:数控铣削平面编程实例 编辑:程序博客网 时间:2024/05/16 10:06

Yet Another Multiple Problem

Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 6291    Accepted Submission(s): 1460


Problem Description
There are tons of problems about integer multiples. Despite the fact that the topic is not original, the content is highly challenging. That’s why we call it “Yet Another Multiple Problem”.
In this problem, you’re asked to solve the following question: Given a positive integer n and m decimal digits, what is the minimal positive multiple of n whose decimal notation does not contain any of the given digits?
 

Input
There are several test cases.
For each test case, there are two lines. The first line contains two integers n and m (1 ≤ n ≤ 104). The second line contains m decimal digits separated by spaces.
Input is terminated by EOF.
 

Output
For each test case, output one line “Case X: Y” where X is the test case number (starting from 1) while Y is the minimal multiple satisfying the above-mentioned conditions or “-1” (without quotation marks) in case there does not exist such a multiple.
 

Sample Input
2345 37 8 9100 10
 

Sample Output
Case 1: 2345Case 2: -1
 

Source

2012 Asia Chengdu Regional Contest

【分析】:

题意给出一个n,和m个数字,求出最小的n的倍数,满足这个数中不含那m个数字中所有的数字。

显而易见这个问题符合广搜的思路,每一个状态能向下扩展出来10-m个状态来,但是一个问题就是我们最后扩展出来的数可能非常大,很有可能就没有头了,这怎么办呢?
我们可以只存余数。对于一个数a,如果a%n=r,那么a所能扩展出来的状态全部可以由r扩展出来,那么如果一个r用过了的话,以后再遇到r的时候就不用考虑了,然后当我们第一个得到了r=0的时候,输出即可。


同余模定理讲解:点击打开链接

这个题上用到的关键点是,大数取模。

例如要求141%3的余数,如果这里不是141,是一个很大的数,是无法直接求模的。

这里需要分块求模,就是分解成一位一位的。

(a+b)%c=(a%c+b%c)%c;
(a*b)%c=(a%c*b%c)%c;

具体做法是:

先求 1 % 3 = 1;

然后余数1:(1*10 + 4)% 3 = 2;

然后余数2:(2*10 + 1)% 3 =0;

余数为0 ,即为141%3==0;

根据这个定理,跑BFS,队列维护装每一次的余数,去进行下一次余除。

同时用mod[]记录刚才加上的位数。用pre[]数组记录上一次的余数。


#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<vector>#include<math.h>#include<queue>#include<set>#define LL long long#define INf 0x3f3f3f3fusing namespace std;int KGCD(int a,int b){if(a==0)return b;if(b==0)return a;if(~a&1){ if(b&1) return KGCD(a>>1,b);else return KGCD(a>>1,b>>1) <<1; } if(~b & 1)  return KGCD(a, b>>1);  if(a > b) return KGCD((a-b)>>1, b);return KGCD((b-a)>>1, a);}  int LCM(int a,int b){ return a/KGCD(a,b)*b; } int num[10];//记录出现的数字int pre[10005];//记录上次的余数 int mod[10005]; //记录刚才加上的位数 int m,n,x;void print(int x)//回溯{if(~x)//从第一个开始输出 {print(pre[x]);printf("%d",mod[x]);}}void bfs(){queue<int>q;for(int i=1;i<10;i++){if(!num[i] && mod[i%n]==-1)//寻找没出现过的 {//找到是当前余数的最小的数值 mod[i%n]=i;//记录下第一次的那个数  方便回溯 (需要输出这个数所以不能去模) q.push(i%n);//放进去第一个数(mod)参考同余定理 第二条  将所有满足条件的放进去 }//同时满足从小到大的区别 }while(!q.empty()){int temp=q.front();q.pop();if(temp==0) //当前就是(本来输入的时候就是就是n的倍数,或者后边不断判断到达n的倍数) {print(temp);//开始找到源头 return ;}for(int i=0;i<10;i++){if(num[i])continue;x=(temp*10+i)%n;if(mod[x]==-1)//判断余数,要小的 {mod[x]=i;pre[x]=temp;q.push(x);}}} printf("-1");}int main(){int count=1;while(scanf("%d%d",&n,&m)!=EOF){memset(pre,-1,sizeof(pre));memset(mod,-1,sizeof(mod));memset(num,0,sizeof(num));for(int i=0;i<m;i++){scanf("%d",&x); num[x]=1;//记录下表示这个数出现过 }printf("Case %d: ",count++);bfs();printf("\n"); }return 0;} 



阅读全文
0 0