银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::
Timus 1007. Code words 讨论在有噪声的信道上的通信问题。

1007. Code words

Time Limit: 2.0 second
Memory Limit: 16 MB
A transmitter sends over a noisy line some binary code words. The receiver on the other end uses special technique to recover the original words.
Every word originally consists of symbols 0 and 1. All words have the same length N (4 ≤ N ≤ 1000). After traveling through the noisy line one of the following modifications to a word may occur:
  1. Any (but only one) symbol 0 is replaced by 1.
  2. Any (but only one) symbol is removed.
  3. A symbol (0 or 1) is inserted at any position.
It is known that the original words all have the following property: the sum of positions where symbols 1 are located is a multiple of (N+1) or equal to zero.

Input

Input contains number N followed by received words. The words are delimited with line breaks. There will be no more than 2001 words. There is nothing else in the input data, except maybe for some extra spaces or line breaks.

Output

Your program should print to output the original sequence of words as they were transmitted. The words should be delimited by line breaks.

Sample

inputoutput
4
0000
011
1011
11011
0000
0110
1001
1111
Problem Source: USU Championship 1997

解答如下:

 1 using System;
 2 
 3 namespace Skyiv.Ben.Timus
 4 {
 5   // http://acm.timus.ru/problem.aspx?space=1&num=1007
 6   sealed class T1007
 7   {
 8     static void Main()
 9     {
10       int n = int.Parse(Console.ReadLine().Trim());
11       byte[] word = new byte[n + 2];
12       int[] cnt = new int[n + 2], sum = new int[n + 2];
13       for (string s; (s = Console.ReadLine()) != null; )
14       {
15         if ((s = s.Trim()).Length == 0continue;
16         for (int i = 0; i < s.Length; i++) word[i + 1= (byte)(s[i] - '0');
17         Initialize(s.Length, word, sum, cnt);
18         if (n < s.Length) Remove(word, sum, cnt);
19         else if (n > s.Length) Insert(word, sum, cnt);
20         else Replace(word, sum);
21         for (int i = 1; i <= n; i++) Console.Write(word[i]);
22         Console.WriteLine();
23       }
24     }
25     
26     static void Initialize(int len, byte[] word, int[] sum, int[] cnt)
27     {
28       for (int i = 1; i <= len; i++)
29       {
30         cnt[i] = cnt[i - 1+ word[i];
31         sum[i] = sum[i - 1+ word[i] * i;
32       }
33     }
34 
35     static void Replace(byte[] word, int[] sum)
36     {
37       if (sum[word.Length - 2% (word.Length - 1== 0return;
38       for (int n = word.Length - 2, i = 1; i <= n; i++)
39       {
40         if (word[i] == 0 || (sum[n] - i) % (n + 1!= 0continue;
41         word[i] = 0;
42         return;
43       }
44     }
45     
46     static void Remove(byte[] word, int[] sum, int[] cnt)
47     {
48       for (int n = word.Length - 2, i = 1; i <= n + 1; i++)
49       {
50         if ((cnt[n+1- cnt[i] + sum[i] - sum[i-1- sum[n+1]) % (n + 1!= 0continue;
51         for (int k = i; k <= n; k++) word[k] = word[k + 1];
52         return;
53       }
54     }
55 
56     static void Insert(byte[] word, int[] sum, int[] cnt)
57     {
58       for (int n = word.Length - 2, i = 1; i <= n; i++)
59       {
60         int v = sum[n - 1+ cnt[n - 1- cnt[i - 1];
61         int bit = (v % (n + 1== 0? 0 : (((v + i) % (n + 1== 0? 1 : -1);
62         if (bit < 0continue;
63         for (int k = n; k > i; k--) word[k] = word[k - 1];
64         word[i] = (byte)bit;
65         return;
66       }
67     }
68   }
69 }

这道题目是说,发送方通过一个有噪声的信道发送二进制编码的单词,所有的单词都具有相同的长度 N (4 ≤ N ≤ 1000)。经过该信道后,每个单词可能产生以下一项变化:

  1. 某个 0 被替换为 1
  2. 删除任何一个 0 或 1
  3. 在任何位置插入 0 或 1

已知原始的单词具有以下性质:字符 1 的位置的和(以下称为校验码)是 N + 1 的倍数。现在要求写一个程序输出原始的单词。

在上述程序中,第 8 到 24 行为程序的入口点 Main 方法,负责依次读入接收到的单词,接着在 17 行调用 Initialize 方法对单词进行预计算,然后在第 18 到 20 行根据接收到的单词的长度分别调用 Remove 、Insert 和 Replace 方法进行处理,最后在第 21 到 22 行输出经过处理后的单词。

第 26 到 33 行的 Initialize 方法计算单词在每个位置上的 1 的个数 cnt 以及校验码 sum。注意 sum[n] 就是整个单词的检验码。

第 35 到 44 行的 Replace 方法处理可能发生的第一种变化(某个 0 被替换为 1)。第 37 行判断如果校验码是 n + 1 的倍数,说明该单词没有发生变化,就直接返回。第 38 行开始的循环遍历整个单词(该单词的长度是 n),试图寻找出发生变化的位置。关键在于第 40 行:

if (word[i] == 0 || (sum[n] - i) % (n + 1) != 0) continue;

首先,如果该位置处的字符是 0 就跳过。否则,该位置处的字符肯定是 1,如果该单词的校验码 sum[n] 减去该位置处本身的校验码 i 是 n + 1 的倍数的话,说明这个位置就是所要寻找的。那么,就在第 41 行将该位置处的字符设置为 0 以去除噪声的干扰。

第 46 到 54 行的 Remove 方法处理第三种变化(在任何位置插入 0 或 1)。第 48 行开始的循环遍历整个单词(该单词的长度是 n + 1),试图寻找出插入的位置。关键在于第 50 行:

if ((cnt[n+1] - cnt[i] + sum[i] - sum[i-1] - sum[n+1]) % (n + 1) != 0) continue;

在这个条件语句的表达式中,cnt[n+1] - cnt[i] 表示在该位置右边的 1 的个数,也就是说,如果在该位置处删除一个字符的话,由此减少的校验码的数值。sum[i] - sum[i-1] 表示该位置处本身的校验码,它实际上可能是 i (该位置上的字符是 1 的话)或 0 (该位置上的字符是 0 的话)。sum[n+1] 表示该单词的校验码,如果它与前两项之和的差值是 n + 1 的倍数的话,说明这个位置就是所要寻找的。那么,就在第 51 行将该位置右边所有的字符左移一位,以删除被噪声插入的字符。

第 56 到 67 行的 Insert 方法处理第二种变化(删除任何一个 0 或 1)。第 58 行开始的循环遍历整个单词(该单词的长度是 n - 1),试图寻找出删除的位置。关键在于第 60 和 61 行:

int v = sum[n - 1] + cnt[n - 1] - cnt[i - 1];
int bit = (v % (n + 1) == 0) ? 0 : (((v + i) % (n + 1) == 0) ? 1 : -1);

在上面的语句中,sum[n - 1] 表示该单词的校验码,cnt[n - 1] - cnt[i - 1] 表示在该位置右边的 1 的个数,也就是说,如果在该位置处插入一个字符的话,由此增加的校验码的数值。如果以上两项之和是 n + 1 的倍数的话,表示需要在该位置插入字符 0 。如果以上两项之和再加上该位置处本身的校验码 i 是 n + 1 的倍数的话,表示需要在该位置插入字符 1 。第 63 行将该位置右边所有的字符右移一位,然后在该位置处插入被噪声删除的字符 0 或 1 (第 64 行)。


返回目录
posted on 2008-12-15 16:00  银河  阅读(1972)  评论(8编辑  收藏  举报