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

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 ≤ 10 4). 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 3 7 8 9 100 1 0
 

Sample Output
  
  
Case 1: 2345 Case 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 0x3f3f3f3f
using 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;
} 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值