hdu1664 Different Digits

链接

http://acm.hdu.edu.cn/showproblem.php?pid=1664

题解

通过写一些暴力,就容易得出这样的猜测:答案是不是最多包含两种数字?
事实的确实这样的,随便用某个数位 x x x,写出 x , x x , x x x , x x x x , x x x x x , . . . , x x x . . . x x x x x,xx,xxx,xxxx,xxxxx,...,xxx...xxxx x,xx,xxx,xxxx,xxxxx,...,xxx...xxxx这样写 n + 1 n+1 n+1个,根据鸽巢原理,肯定存在两个数关于 n n n同余,那么它们的差就肯定是 n n n的倍数,而这个数只包含 0 0 0 x x x两种数位,所以肯定存在一个有包含两种数位的答案。
先判断有没有只包含一种数位的答案
直接能想到的就是构造数位 x x x,然后判断 x , x x , x x x . . . x,xx,xxx... x,xx,xxx...是不是模 n n n 0 0 0,然而枚举到多少位呢?
仔细考虑这个过程,我们是用一个临时变量 t m p tmp tmp,存储当前数字对 n n n的余数,然后tmp = tmp*10+x
注意到,如果 t m p tmp tmp和之前重复出现了,那么肯定就面临一个循环节,也就是说后面的操作都和之前重复了,因此没必要再枚举下去,而 t m p tmp tmp的所有取值的可能就只有 n n n种,因此我可以一发现循环就直接退出,这样复杂度最坏也只是 O ( n ) O(n) O(n)
考虑包含两种数位的答案
假设两种数位是 x , y x,y x,y
我还是用一个 t m p tmp tmp表示一个由 x x x y y y写成的数字对 n n n的余数
和之前不同的是,一个 t m p tmp tmp又会延伸出两种选择
这个时候我们需要 B F S BFS BFS,这样能够保证我们搜出来的是最小的
开一个访问标志数组,存储一下这个余数出现过没有,因为是 B F S BFS BFS所以后出现的肯定不如前出现的更优
这样复杂度依然是 O ( n ) O(n) O(n)

代码

//数论、搜索
#include <bits/stdc++.h>
#define maxn 70000
#define ll long long
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
ll q[maxn], head, tail, pre[maxn], N, dig[2], d[maxn];
bool vis[maxn];
ll read(ll x=0)
{
	ll c, f=1;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
	for(;isdigit(c);c=getchar())x=x*10+c-48;
	return f*x;
}
ll BFS(ll k)
{
	ll i, x, tmp;
	head = tail = 1;
	cl(vis), cl(pre);
	for(i=0;i<k;i++)if(dig[i])d[tail] = dig[i], q[tail++]=dig[i]%N, vis[dig[i]%N]=true;
	while(head<tail)
	{
		x = q[head++];
		if(x==0)return head-1;
		for(i=0;i<k;i++)
		{
			tmp = (x*10+dig[i])%N;
			if(!vis[tmp])
			{
				vis[tmp] = true;
				pre[tail] = head-1;
				d[tail] = dig[i];
				q[tail++] = tmp;
			}
		}
	}
	return -1;
}
string get_string(ll last)
{
	string ans = "";
	for(ll p = last;p;p=pre[p])
	{
		ans += char(0x30+d[p]);
	}
	reverse(ans.begin(),ans.end());
	return ans;
}
int main()
{
	ll i, j, rax;
	string ans, tmp;
	while(N = read())
	{
		ans = "-1"; 
		for(i=1;i<=9;i++)
		{
			dig[0] = i;
			rax = BFS(1);
			if(rax != -1)
			{
				tmp = get_string(rax);
				if(ans == "-1")ans = tmp;
				else if(ans.size()>tmp.size())ans = tmp;
			}
		}
		if(ans == "-1")
		{
			for(i=0;i<=9;i++)for(j=i+1;j<=9;j++)
			{
				dig[0] = i, dig[1] = j;
				rax = BFS(2);
				if(rax != -1)
				{
					tmp = get_string(rax);
					if(ans == "-1")ans = tmp;
					else if(ans.size()>tmp.size() or ans.size()==tmp.size() and ans>tmp)ans = tmp;
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值