【学算法的辅助练习3】北大POJ2956:Repeatless Numbers

本文介绍了北大在线判题系统POJ的2956题 Repeatless Numbers,主要讨论如何通过枚举和优化方法解决这个问题。内容包括题目描述、分析、进阶解法(如使用BFS和位操作优化)以及代码实现。
摘要由CSDN通过智能技术生成

题目列表

一、枚举:1753、2965

二、贪心:2109、2586

三、最小生成树:2485、1258

四、排序:2388

五、深度优化搜索:1321、2251

六、广度优化搜索:3278、1426

七、简单搜索技巧和剪枝:2676

八、背包问题:1837

九、拓扑排序:1094

十、最短路径算法:1062、1125、2240

十一、哈夫曼树:3253

十二、哈希表和二分查找等高效查找法:2151、2503
 

本周继续练习枚举。

北大POJ2956:Repeatless Numbers

题目

传送门

分析

输入:输入测试文件将包含多个测试用例,每个测试用例由包含整数 n 的单个行组成,其中 1 ≤ n ≤ 1000000。文件末尾由 n = 0 的测试用例标记,不应进行处理。

输出:对于每个输入情况,程序应在一行上打印第 n 个无重复数字。

示例输入:

25

10000

0

示例输出:

27

26057

我的思路:

算是暴力解法,枚举+分类判断是否满足条件。

 

进阶

1.用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中存在,否则不存在。

2.考虑一些情况来减少枚举

创建一个数组,保存每一位,判断有无重复数字(这里感觉有点像第一章说的位存储)。比如对于一个数128267来讲,它的第2位(从右往左数)和第4位是相同的,则形如1282**的数都不用枚举了,直接从128300枚举即可,这样可加快枚举的时间。

代码 

1.用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 }

2.减少枚举:

 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 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值