[アルゴリズム学習のための補助演習 3] 北京大学 POJ2956: 反復しない数

トピックリスト

1.列挙: 1753、2965

2. 貪欲: 2109、2586

3. 最小スパニングツリー: 2485、1258

4. 仕分け: 2388

5.詳細な最適化検索: 1321、2251

6. 幅の最適化された検索: 3278、1426

7. 簡単な検索テクニックと枝刈り: 2676

8. ナップサック問題: 1837年

9. トポロジカルソート: 1094

10. 最短パスアルゴリズム: 1062、1125、2240

11. ハフマンツリー: 3253

12. ハッシュテーブルや二分探索などの効率的な検索方法:2151、2503
 

今週も列挙の練習を続けます。

北京大学 POJ2956: 繰り返されない数字

トピック

ポータル

分析する

入力: 入力テスト ファイルには複数のテスト ケースが含まれます。各テスト ケースは、整数n  (1 ≤  n  ≤ 1000000)を含む 1 行で構成されます 。ファイルの終わりは、  n  = 0 のテスト ケースによってマークされているため、処理しないでください。

出力: 各入力ケースについて、プログラムはn 番目 の一意の番号を1 行に 出力する必要があります。

入力例:

25

10000

0

出力例:

27

26057

私の思考回路:

条件を満たしているかどうかを判断する列挙+分類という乱暴な解決法です。

 

高度

1.BFSを使用します

BFS を使用するとはまったく予想していませんでした。

——初期状態は 1 ~ 9 で、初期状態の後に 0 ~ 9 を連続的に加算することで次の階層状態を取得でき、拡張が 1000000 番目の状態に達するまで 2 番目の階層状態が継続的に拡張されます。各状態には 2 つの属性値と数字が含まれます。値は状態の値です。134 であると仮定すると、数字は 11010 になります。d[i]=1 は、値に 0 ~ 9 の数字が追加されることを意味します。数字 134 (加算した数を 1 とします)、134 に 1 が出現したかどうかを判断する方法、値 = 1、桁 = 10、および 11010 と 10 の演算は 11010&10 です。結果が 0 に等しくない場合、これは、134 の中に 1 が存在することを意味し、それ以外の場合は存在しません。

2.列挙を減らすためにいくつかのケースを考慮します。

配列を作成し、各ビットを保存し、繰り返しの数値があるかどうかを判断します(ここは第 1 章で述べたビットの保存に少し似ています)。たとえば、数値 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 }

おすすめ

転載: blog.csdn.net/weixin_43594181/article/details/123248149