poj2956 Repeatless Numbers(枚举|BFS)
题目链接
http://poj.org/problem?id=2956
题意
如果一个数中的每一位都是不同的,那么这个数叫做无重复数,如11是有重复数,12是无重复数。输入正整数n(1<=n<=1000000),输出第n个无重复数。
思路
可以使用两种方法来解决本题:枚举和bfs。
方法一:枚举
如果直接暴力枚举的话会超时,所以要考虑一些情况来减少枚举。比如对于一个数128267来讲,它的第2位(从右往左数)和第4位是相同的,则形如1282**的数都不用枚举了,直接从128300枚举即可,这样可加快枚举的时间。
枚举代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 1000000; 7 int ans[N+1] = {0}; 8 int power[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; 9 10 void init() 11 { 12 int d[10]; //存储数字x 13 int v[10]; //v[i]表示数字i是否在x中出现过 14 int cur = 1; 15 int x, y=1; 16 while(cur<=N) 17 { 18 x = y; 19 memset(v, -1, sizeof(v)); 20 memset(d, 0, sizeof(d)); 21 22 int i, j; 23 for(i=0; x!=0; i++) 24 { 25 d[i] = x % 10; 26 if(v[d[i]]!=-1) 27 break; 28 v[d[i]] = i; 29 x /= 10; 30 } 31 if(!x) 32 { 33 ans[cur++] = y; 34 y++; 35 } 36 else 37 { 38 j = v[d[i]]; 39 for(i--; i>=j; i--) 40 x = x*10+d[i]; 41 x++; 42 y = x*power[j]; 43 } 44 } 45 } 46 47 int main() 48 { 49 init(); 50 int n; 51 while(cin>>n && n) 52 cout<<ans[n]<<endl; 53 return 0; 54 }
方法二:BFS
bfs的初始状态为1~9,在初始状态的后面不断地添加0~9可以得到下一层状态,在对第二层状态不断地进行扩展,知道扩展到第1000000个状态为止。每一个状态包含两个属性value和digit,value为状态的值,假设为134,则digit为11010,d[i]=1表示i在value中,在数字134后添加0~9中的数字时(假设添加的数字为1),如何判断1是否在134中出现过呢,value=1,则digit = 10 ,将11010与10做与操作即11010&10,若结果不等于0,这说明1在134中存在,否则不存在。
BFS代码
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 1000000; 7 struct Number 8 { 9 int value; //数字的十进制表示 10 int digit; //二进制序列,从右往左数digit[i]==1表示value中有i 11 12 Number(){} 13 Number(int v, int d):value(v), digit(d){} 14 }ans[N+10]; 15 16 int main() 17 { 18 for(int i=1; i<10; i++) 19 ans[i] = Number(i, 1<<i); 20 21 int k = 1; 22 for(int cur=10; cur<=N; k++) 23 { 24 int v = ans[k].value; 25 int d = ans[k].digit; 26 27 for(int i=0; i<10; i++) 28 { 29 if( !(d & (1<<i))) 30 ans[cur++] = Number(v*10+i, d|1<<i); 31 } 32 } 33 34 int n; 35 while(scanf("%d", &n)==1 && n) 36 printf("%d\n", ans[n].value); 37 return 0; 38 }
一点总结
如何判断数a和数b中是否存在相同的数字呢?
一种做法是将数a和数b的每一位分别分解到数组A[]和数组B[]中,然后遍历数组A[]、B[],试着找出相同的位。
第二种做法是用A[i]=1表示i存在a中,A[i]=0,表示a中没有i,则对处理后的A[]和B[]进行与(&)操作,结果为0说明a,b中没有相同的位,结果为1则说明a,b中有相同的位。在上面的bfs中,如果每一个状态都存储一个数组的话,会消耗大量内存,所以可以将数组中的二进制序列转化成一个十进制整数,通过左移i位来表示数字中包含i。
参考