星辰.Net技术社区论坛

首页 » .NET » 算法/数据结构 » 要求给出第 K (0 < K < 109) 个 N (0 < N < 44) 位二进制数,该二进
admin - 2008-7-3 22:13:00
InputThe first line of input contains two positive integers N and K. s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
OutputWrite the found sequence or −1 if the number K is larger then the number of valid sequences. s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Sample
inputoutput
3 1 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
000 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Problem Author: Emil Kelevedzhiev s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Problem Source: Winter Mathematical Festival Varna '2001 Informatics Tournament 答案如下: s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
1 using System; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
2 using System.IO; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
3 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
4 namespace Skyiv.Ben.Timus s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
5 { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
6  //http://acm.timus.ru/problem.aspx?space=1&num=1081 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
7  class T1081 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
8   { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
9      static void Main() s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
10     { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
11      new T1081().Run(Console.In, Console.Out); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
12     } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
13 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
14    void Run(TextReader reader, TextWriter writer) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
15     { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
16      string[] ss = reader.ReadLine().Split(); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
17      short[] bs = GetBinarys(int.Parse(ss[0]), int.Parse(ss[1])); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
18      if (bs == null) writer.Write(-1); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
19      else for (int i = bs.Length - 1; i >= 0; i--) writer.Write(bs); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
20     } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
21 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
22    short[] GetBinarys(int n, int k) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
23     { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
24      short[] bs = new short[n]; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
25      int[] fib = GetFibonacci(n); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
26      for (int i = n + 2; k > 1; k -= fib) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
27       { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
28         i = Seek(fib, i - 2, k); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
29        if (i >= n) return null; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
30         bs = 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
31       } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
32      return bs; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
33     } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
34 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
35    int[] GetFibonacci(int n) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
36     { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
37      int[] fib = new int[n + 1]; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
38       fib[0] = 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
39       fib[1] = 2; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
40      for (int i = 2; i <= n; i++) fib = fib[i - 1] + fib[i - 2]; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
41      return fib; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
42     } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
43 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
44    int Seek(int[] fib, int m, int k) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
45     { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
46      for (int i = m; i >= 0; i--) if (fib < k) return i; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
47      return -1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
48     } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
49   } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
50 } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
这道题要求给出第 K (0 < K < 109) 个 N (0 < N < 44) 位二进制数,该二进制数不得有相邻的“1”。由于时间限制是 0.5 秒,肯定不能使用蛮力搜索从 1 列举到 K。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
我们以 N = 5 来分析看看有没有什么规律。如左图所示,我们发现该二进制数最左边的“1”开始在第几个数之后出现是很规律的,如下所示(左图中红色粗框中的数): s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
1, 2, 3, 5, 8, 13, ... s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
也就是说,后项等于前二项之和。这不正是扔掉第一项后的斐波那契 ( Fibonacci ) 数列吗? s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
于是,程序在第 35 到 42 行的 GetFibonacci() 方法中计算出该数列。然后在第 22 到 33 的 GetBinarys() 方法中计算出第 K 个满足条件的二进制数。该方法在第 26 到 31 行的循环中从高位到低位设定“1”(如左图中红色细框所示)。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
请注意,左图中两块阴影部分的内容是相同的,都代表了 N = 3 的情况。也就是说,N = 5 的最低三位是在重复 N = 3 的情况。并且由于该二进制数不得有相邻的“1”,所以在程序第 28 行使用 i - 2 而不是 i - 1 作为第 2 个参数调用 Seek() 方法,然后在第 30 行将该二进制数的第 i 位设为“1”。最后在第 26 行 k -= fib,以进入下一轮循环,直到 k 降低到 1 而结束循环。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
本程序的算法应该是最优的,其时间复杂度为 O(N)。本程序的运行时间是 0.078 秒,其 C++ 版本程序的运行时间是 0.001 秒。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
我们知道,斐波那契 ( Fibonacci ) 数列定义如下: s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
F1 = 1, F2 = 1, Fn = Fn-1 + Fn-2  (n > 2) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
她的前几项如下: s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ... s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
她的通项公式如下: s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Fn = ( (1+ √5) / 2 )n / √5 - ( (1- √5) / 2 )n / √5 ≈ O(1.618n) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
由于程序中的 fib 数组的下标是从 0 开始,且扔掉了斐波那契 ( Fibonacci ) 数列的第一项,所以 fib[n] = Fn+2。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
从上图中可以看出,N 位没有相邻的“1”的二进制数共有 fib[N] 个,也就是 FN+2 ≈ O(1.618N+2) 个。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
所以,如果使用蛮力搜索从 1 列举到 K,其时间复杂度约为 O(1.618N+2)。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
在本题中,要求给出第 K (0 < K < 109) 个 N (0 < N < 44) 位没有相邻的“1”的二进制数。实际上 fib[43] = F45 = 1,134,903,170 ≈ 109。所以 K 和 N 的大小是匹配的。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
我用 C++ 语言写了一个蛮力搜索从 1 列举到 K 的程序,如下: s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
1 // http://acm.timus.ru/problem.aspx?space=1&num=1081 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
2 #include <iostream> s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
3 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
4 const int N_MAX = 43; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
5 __int64 mask[N_MAX + 1]; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
6 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
7 void init(int n) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
8 { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
9   mask[0] = 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
10 for (int i = 1; i <= n; i++) mask = mask[i - 1] << 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
11 } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
12 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
13 int valid(__int64 bin, int n) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
14 { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
15  while (n >= 0 && (bin & mask[n]) == 0) n--; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
16  while (n > 0) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
17   { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
18    if (bin & mask[--n]) return 0; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
19    while (n >= 0 && (bin & mask[n]) == 0) n--; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
20   } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
21  return 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
22 } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
23 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
24 __int64 getBinary(int n, int k) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
25 { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
26   __int64 bin = 0; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
27  for ( ; k > 0; bin++) s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
28   { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
29    if (bin >= mask[n]) return -1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
30    if (valid(bin, n - 1)) k--; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
31   } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
32  return bin - 1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
33 } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
34 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
35 int main() s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
36 { s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
37  int n, k; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
38   std::cin >> n >> k; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
39   init(n); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
40   __int64 bin = getBinary(n, k); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
41  if (bin < 0) std::cout << -1; s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
42  else for (int i = n - 1; i >= 0; i--) std::cout << ((bin & mask) ? 1 : 0); s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
43 } s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
将该程序提交到 Timus Online Judge,果然超时 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
上图中,ID 为 2144627 的记录(运行到 0.515 秒后因为超时而被终止)就是蛮力搜索程序的运行结果,而 ID 为 2141944 的记录(运行时间为 0.001 秒)就是将前面 C# 程序翻译为 C++ 程序的运行结果。 s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
1
查看完整版本: 要求给出第 K (0 < K < 109) 个 N (0 < N < 44) 位二进制数,该二进