计算 5*n 板的正确配置(动态编程,C++)

Counting Correct Configurations of a 5*n board (Dynamic Programming, C++)

问题如下: 编写一个程序来确定 5*n 方形网格上 white/black 模式的数量(模数 m),其中不包含任何 p 禁止模式。

输入数据具有以下格式:第一行包含 3 个逗号分隔的整数: n - 列数,p - 禁止模式数,m - 模数。以下 3*p 行包含 3 个字符长的字符串,由 .x 组成,其中 . 表示白色区域,x 表示黑色区域。这些是禁止的模式,例如:

x..
..x
.xx

这是我解决这个问题的动态算法:

让 3 列 "block" 由 15 位长的位集表示,位映射到列如下(1 == 黑色,0 == 白色):

0 5 10
1 6 11
2 7 12
3 8 13
4 9 14

读取输入数据并构造所有表示禁止模式的位集(未使用的字段设置为 0)。创建一个布尔值向量,并在每个索引下使用等于禁止模式的二进制表示将值设置为 true。对表示向左移动 1 和 2 的禁止模式的索引执行相同操作(因为禁止模式可能从列的第一行、第二行或第三行开始)。

现在,创建 2 个整数向量,leftright,每个向量的大小为 1024 (2^10)。这些向量中的索引表示 2 列的配置 "blocks"。在第 i 次迭代中,left 向量中的值表示有多少不同的正确配置以第 (i-1) 列中该向量的索引表示的 2 列模式结束。现在遍历第 3 列的所有 32 种可能性,并将该列添加到 2 列模式,每次检查这样的 3 列模式是否不包含禁止模式。如果是,则跳到第 3 列的下一个可能性,如果不是,则从表示 3 列模式的前 2 列的 left 向量中获取值并将其添加到 right 表示由第 2 列和第 3 列组成的 2 列模式的向量。在考虑了所有可能的 2 列之后,交换向量,用 0 初始化 right 向量并重复该过程,直到我们到达 n-th 列。

答案是左侧向量中所有字段的总和(因为最后一次操作交换了向量)。

我希望这是清楚的,如果有任何需要澄清的地方,请发表评论。

我的实施 returns 仅当禁用模式在顶行和底行中至少有 1 个 x 时才能得到正确的结果,即:

x..
...
..x

我只是想不通问题出在哪里。如果代码中有任何不清楚的地方,请告诉我,我将编辑此 post 以澄清这一点。

这是我的实现: http://pastebin.com/2ZPjiyuj

#include <iostream>
#include <vector>
#include <bitset>
#include <stdio.h>

using namespace std;

ostream& operator<< ( ostream& os, const vector<uint32_t>& v )
{
  for( auto it = v.begin(); it != v.end(); it++ )
  {
    os << *it << "\n";
  }
  return os;
}

inline bitset<15> make_pattern()
{
  char field;
  bitset<15> pattern;

  for( uint32_t offset = 0; offset < 3; offset++ )
  {
    cin >> field;
    if( field == 'x' ) pattern.set(offset);

    cin >> field;
    if( field == 'x' ) pattern.set(offset + 5);

    cin >> field;
    if( field == 'x' ) pattern.set(offset + 10);
  }
  return pattern;
}

int main()
{
  uint32_t n, m, p;
  cin >> n;
  cin >> p;
  cin >> m;

  vector<bool> forbidden_patterns( 33000 );

  for( uint32_t i = 0; i < p; i++ )
  {
    auto pattern = make_pattern();
    forbidden_patterns[pattern.to_ulong()] = true;  // true := forbidden; false := allowed.
    forbidden_patterns[(pattern << 1).to_ulong()] = true;
    forbidden_patterns[(pattern << 2).to_ulong()] = true;
    //cout << forbidden_patterns[(pattern<<3).to_ulong()];
    //cout << pattern.to_ulong();
  }

  for( uint32_t i = 0; i< 33000; i++ ) //checking the contents of the forbidden_patterns vector
  {
    bitset<15> bs = i;
    if(forbidden_patterns[i] == true)
      cout << bs << "\n";
  }
  cout << "\n\n";

  //bitmasks for setting 2 rows of a 3 column pattern to 0;
  bitset<15> bottom_rows_reset_mask;
  bottom_rows_reset_mask.set(3); bottom_rows_reset_mask.set(8); bottom_rows_reset_mask.set(13);
  bottom_rows_reset_mask.set(4); bottom_rows_reset_mask.set(9); bottom_rows_reset_mask.set(14);
  bottom_rows_reset_mask = ~bottom_rows_reset_mask;

  bitset<15> top_rows_reset_mask;
  top_rows_reset_mask.set(0); top_rows_reset_mask.set(5); top_rows_reset_mask.set(10);
  top_rows_reset_mask.set(1); top_rows_reset_mask.set(6); top_rows_reset_mask.set(11);
  top_rows_reset_mask = ~top_rows_reset_mask;

  bitset<15> top_and_bottom_reset_mask;
  top_and_bottom_reset_mask.set(0); top_and_bottom_reset_mask.set(5); top_and_bottom_reset_mask.set(10);
  top_and_bottom_reset_mask.set(4); top_and_bottom_reset_mask.set(9); top_and_bottom_reset_mask.set(14);
  top_and_bottom_reset_mask = ~top_and_bottom_reset_mask;

  vector<uint32_t> left( 1024, 1 );
  vector<uint32_t> right( 1024, 0 );

  for( uint32_t column = 3; column <= n; column++ )
  {
    for( uint32_t first_2_columns = 0; first_2_columns < 1024; first_2_columns++ )
    {
      if( left[first_2_columns] == 0 ) continue;
      for( uint32_t third_column = 0; third_column < 32; third_column++ )
      {
        bitset<15> t_patt = (first_2_columns | third_column << 10) & top_and_bottom_reset_mask.to_ulong();
        //cout << t_patt << "\n"; //getchar();
        if( forbidden_patterns[t_patt.to_ulong()] == true )
        {
          //cout << t_patt << "\n"; getchar();
          continue;
        }

        t_patt = (first_2_columns | third_column << 10) & bottom_rows_reset_mask.to_ulong();
        //cout << t_patt << "\n"; //getchar();
        if( forbidden_patterns[t_patt.to_ulong()] == true )
        {
          //cout << t_patt << "\n"; getchar();
          continue;
        }

        t_patt = (first_2_columns | third_column << 10) & top_rows_reset_mask.to_ulong();
        //cout << t_patt << "\n"; //getchar();
        if( forbidden_patterns[t_patt.to_ulong()] == true )
        {
          //cout << t_patt << "\n"; getchar();
          continue;
        }

        t_patt = first_2_columns | third_column << 10;
        //cout << t_patt << "\n";
        auto next_2_column_pattern = (t_patt >> 5).to_ulong();
        //t_patt = next_2_column_pattern; cout << t_patt << "\n"; getchar();
        right[next_2_column_pattern] = (right[next_2_column_pattern] + left[first_2_columns]) % m;
      }
    }
    left.swap(right);
    right.assign(1024, 0u);
  }
  uint32_t sum = 0;
  for( int i = 0; i < 1024; i++ )
    sum = (sum + left[i]) % m;

  cout << sum;
}

我已经设法自己解决了这个问题(尽管 PaulMcKenzie 非常感谢你为我指明了正确的方向)。

问题是我的算法没有区分向左移动 0、1 或 2 位的禁止模式。当输入数据的模式在底部和顶部的行中仅包含至少 1 个 x 时,一切都很好,但是一旦在顶部或底部的行中给出了仅包含 .'s 的模式,程序就会开始出现异常。

考虑一个顶行没有任何 x 的禁止模式 - 将其向左移动 1 位后,该模式的前两行变为零。我程序中最内层循环的一个 ifs 将被测试模式的前 2 行设置为零,并将剩余的内容与 forbidden_patterns 向量进行比较,其中它可能与其中一个模式匹配,而没有任何 x's顶行向左移动 1 位!这在逻辑上是错误的,因为已向左移动 1 位的禁止模式从第 2 行开始,而前两行为零的模式只能包含从第 3 行开始的禁止模式。这导致我的程序更频繁地进入最内层循环的 ifs,因此 "under-counting" 正确的配置。

这个问题的解决方案非常简单 - 将 forbidden_patterns 向量分成 3 个向量 - 1 个包含没有任何移位的模式,1 个包含向左移位 1 的模式,第 3 个包含向左移位 2 的模式左边。然后将在最内层循环的 ifs 中使用适当的向量。

固定代码: http://pastebin.com/WCb8eB9F

#include <iostream>
#include <vector>
#include <bitset>
#include <stdio.h>

using namespace std;

inline bitset<15> make_pattern()
{
  char field;
  bitset<15> pattern;

  for( uint32_t offset = 0; offset < 3; offset++ )
  {
    cin >> field;
    if( field == 'x' ) pattern.set(offset);

    cin >> field;
    if( field == 'x' ) pattern.set(offset + 5);

    cin >> field;
    if( field == 'x' ) pattern.set(offset + 10);
  }
  return pattern;
}

int main()
{
  uint32_t n, m, p;
  cin >> n;
  cin >> p;
  cin >> m;

  vector<bool> top_forbidden_patterns( 32768 ); // top_forbidden patterns can only be compared to 3 column patterns that have the top 2 rows zeroed.
  vector<bool> mid_forbidden_patterns( 32768 ); // mid_forbidden patterns can only be compared to 3 column patterns that have the top and bottom row zeroed.
  vector<bool> bottom_forbidden_patterns( 32768 ); // bottom_forbidden patterns can only be compared to 3 column patterns that have the bottom 2 rows zeroed.

  for( uint32_t i = 0; i < p; i++ )
  {
    auto pattern = make_pattern();
    bottom_forbidden_patterns[pattern.to_ulong()] = true;  // true := forbidden; false := allowed.
    mid_forbidden_patterns[(pattern << 1).to_ulong()] = true;
    top_forbidden_patterns[(pattern << 2).to_ulong()] = true;
  }

  //bitmasks for setting 2 rows of a 3 column pattern to 0;
  bitset<15> bottom_rows_reset_mask;
  bottom_rows_reset_mask.set(3); bottom_rows_reset_mask.set(8); bottom_rows_reset_mask.set(13);
  bottom_rows_reset_mask.set(4); bottom_rows_reset_mask.set(9); bottom_rows_reset_mask.set(14);
  bottom_rows_reset_mask = ~bottom_rows_reset_mask;

  bitset<15> top_rows_reset_mask;
  top_rows_reset_mask.set(0); top_rows_reset_mask.set(5); top_rows_reset_mask.set(10);
  top_rows_reset_mask.set(1); top_rows_reset_mask.set(6); top_rows_reset_mask.set(11);
  top_rows_reset_mask = ~top_rows_reset_mask;

  bitset<15> top_and_bottom_reset_mask;
  top_and_bottom_reset_mask.set(0); top_and_bottom_reset_mask.set(5); top_and_bottom_reset_mask.set(10);
  top_and_bottom_reset_mask.set(4); top_and_bottom_reset_mask.set(9); top_and_bottom_reset_mask.set(14);
  top_and_bottom_reset_mask = ~top_and_bottom_reset_mask;

  vector<uint32_t> left( 1024, 1 );
  vector<uint32_t> right( 1024, 0 );

  for( uint32_t column = 3; column <= n; column++ )
  {
    for( uint32_t first_2_columns = 0; first_2_columns < 1024; first_2_columns++ )
    {
      if( left[first_2_columns] == 0 ) continue;
      for( uint32_t third_column = 0; third_column < 32; third_column++ )
      {
        bitset<15> t_patt = (first_2_columns | third_column << 10) & top_and_bottom_reset_mask.to_ulong();
        if( mid_forbidden_patterns[t_patt.to_ulong()] == true )
          continue;

        t_patt = (first_2_columns | third_column << 10) & bottom_rows_reset_mask.to_ulong();
        if( bottom_forbidden_patterns[t_patt.to_ulong()] == true )
          continue;

        t_patt = (first_2_columns | third_column << 10) & top_rows_reset_mask.to_ulong();
        if( top_forbidden_patterns[t_patt.to_ulong()] == true )
          continue;

        t_patt = first_2_columns | third_column << 10;
        auto next_2_column_pattern = (t_patt >> 5).to_ulong();
        right[next_2_column_pattern] = (right[next_2_column_pattern] + left[first_2_columns]) % m;
      }
    }
    left.swap(right);
    right.assign(1024, 0u);
  }
  uint32_t sum = 0;
  for( auto el : left )
    sum = (sum + el) % m;

  cout << sum;
  return 0;
}