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
", 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。

参考

http://www.acmsearch.com/article/show/24772

原文地址:https://www.cnblogs.com/sench/p/7827374.html