Algorithm 理解具有位移位和异或的五维DP?

Algorithm 理解具有位移位和异或的五维DP?,algorithm,bit-manipulation,dynamic-programming,Algorithm,Bit Manipulation,Dynamic Programming,我正在查看问题的解决方案,但我不太了解动态规划(DP)是如何工作的 问题总结如下:您将获得一个由1或0组成的9x9网格,按如下方式排列在九个3x3子网格中: 000 000 000 001 000 100 000 000 000 000 110 000 000 111 000 000 000 000 000 000 000 000 000 000 000 000 000 您需要找到所需更改的最小数量,以便九行、列和3x3子网格中的每一行都包含偶数个1。在这里,更改被定义为将给定元素从1切

我正在查看问题的解决方案,但我不太了解动态规划(DP)是如何工作的


问题总结如下:您将获得一个由1或0组成的9x9网格,按如下方式排列在九个3x3子网格中:

000 000 000
001 000 100
000 000 000

000 110 000
000 111 000
000 000 000

000 000 000
000 000 000
000 000 000
您需要找到所需更改的最小数量,以便九行、列和3x3子网格中的每一行都包含偶数个1。在这里,更改被定义为将给定元素从1切换到0,反之亦然


该解决方案涉及动态规划,每个状态由最少的移动次数组成,这样,到当前正在查看的行为止的所有行都具有偶数奇偶性(偶数个)

不过,我不明白这些措施的实施细节。首先,在他们的记忆阵列中

int memo[9][9][1<<9][1<<3][2];

int memo[9][9][1我想我已经解决了这个问题。我们的想法是从上到下,从左到右扫描。在每一步中,我们都会通过将当前框设置为0或1来尝试移动到下一个位置

在每一行的末尾,如果奇偶校验为偶数,则我们移动到下一行;否则我们回溯。在每三行的末尾,如果所有三个框的奇偶校验为偶数,则我们移动到下一行;否则我们回溯。最后,在板的末尾,如果所有列的奇偶校验为偶数,则我们完成;否则我们回溯

任何一点的递归状态都可以用以下五条信息来描述:

  • 当前行和列
  • 所有列的平价
  • 我们当前所在的三个框的平价(每行与三个框相交)
  • 列的当前奇偶校验
这是备忘表的外观:

int memo[9][9][1<<9][1<<3][2];
         ^  ^    ^     ^   ^
         |  |    |     |   |
   row --+  |    |     |   |
   col -----+    |     |   |
column parity ---+     |   |
  box parity ----------+   |
current row parity---------+

int-memo[9][9][1解决方案中的算法是一个穷举的深度优先搜索,并进行了一些优化。不幸的是,描述没有准确地解释它

穷举搜索意味着我们尝试枚举所有可能的位组合。深度优先意味着我们首先尝试将所有位设置为一,然后将最后一位设置为零,然后将第二位设置为最后一位,然后将最后一位和第二位都设置为最后一位,以此类推

第一个优化是在检测到奇偶校验不均匀时立即回溯。因此,例如,当我们开始搜索并到达第一行时,我们检查该行是否具有零奇偶校验。如果没有,我们将不继续。我们停止,回溯,并尝试将行中的最后一位设置为零

第二个优化类似于DP,因为我们缓存部分结果并重新使用它们。这利用了一个事实,即就问题而言,搜索中的不同路径可以收敛到相同的逻辑状态。什么是逻辑搜索状态?解决方案中的描述开始解释它(“开始”是关键字)本质上,诀窍在于,在搜索的任何给定点,额外位翻转的最小数量并不取决于整个数独板的确切状态,而只取决于我们需要跟踪的各个平价的状态。(见下面的进一步解释。)我们正在跟踪27个平价(占9列、9行和9个3x3方框)。此外,我们可以优化其中一些。根据我们执行搜索的方式,所有较高行的奇偶校验将始终为偶数,而所有较低行的奇偶校验(尚未被搜索触及)不会改变。我们只跟踪1行的奇偶校验。按照相同的逻辑,忽略上面和下面框的奇偶校验,我们只需要确认“活动”3个框

因此,我们只有2^9*2^9*2^9=134217728个状态,而不是2^9*2^1*2^3=8192个状态。不幸的是,搜索中的每个深度级别都需要单独的缓存。因此,我们将81个可能的深度乘以搜索,发现我们需要一个大小为663552的数组。要借用templatetypedef:

int memo[9][9][1<<9][1<<3][2];
         ^  ^    ^     ^   ^
         |  |    |     |   |
   row --+  |    |     |   |
   col -----+    |     |   |
column parity ---+     |   |
  box parity ----------+   |
current row parity---------+

1<<9 simply means 2^9, given how integers and bit shifts work.

int memo[9][9][1我认为你需要在这个问题上提供更多的背景资料。通常认为,将人们发送到其他网站并期望他们在回答之前消化所有信息是不礼貌的。你能总结一下这个问题以及你目前对解决方案的了解吗?@templatetypedef background info addedMy two cents':链接代码的文档记录很差,很难破译。请编写比这更好的代码!这是一场优化竞赛,还是任何可行的解决方案都可以?@AleksandrDubinsky运行时间限制约为1秒啊,我开始理解了——非常感谢!不过还有一个问题——他们的代码注释状态是
Try se将单元格设置为1。
尝试将单元格设置为0。
但如果我理解正确,他们实际上在做的是尝试两种选择:翻转当前状态并将其保留。对吗?此外,数组中的每个元素表示达到数组中指定的状态所需的移动次数ght?这个值在哪一点递增?@user2612743不,它是按照它说的做的。如果位为0,!A[]计算一个(即,额外翻转),但是如果它已经是1,!A[]不计算。哇!我希望我能接受你两个很好的答案。这可能需要我一天左右的时间(当我有时间的时候)再次感谢您的努力!
int memo[9][9][1<<9][1<<3][2];
         ^  ^    ^     ^   ^
         |  |    |     |   |
   row --+  |    |     |   |
   col -----+    |     |   |
column parity ---+     |   |
  box parity ----------+   |
current row parity---------+

1<<9 simply means 2^9, given how integers and bit shifts work.
/* Try setting the cell to 1. */
ref = !A[r][c] + solve(r, c + 1, mc ^ 1 << c, mb ^ 1 << c / 3, !p);

/* Try setting the cell to 0. */
ref = min(ref, A[r][c] + solve(r, c + 1, mc, mb, p));
/* Try having this cell equal 0 */
bool areWeFlipping = A[r][c] == 1;
int nAdditionalFlipsIfCellIs0 = (areWeFlipping ? 1 : 0) + solve(r, c + 1, mc, mb, p); // Continue the search

/* Try having this cell equal 1 */
areWeFlipping = A[r][c] == 0;
// At the start, we assume the sudoku board is all zeroes, and therefore the column parity is all even. With each additional cell, we update the column parity with the value of tha cell. In this case, we assume it to be 1.
int newMc = mc ^ (1 << c); // Update the parity of column c. ^ (1 << c) means "flip the bit denoting the parity of column c"
int newMb = mb ^ (1 << (c / 3)); // Update the parity of 'active' box (c/3) (ie, if we're in column 5, we're in box 1)
int newP = p ^ 1; // Update the current row parity
int nAdditionalFlipsIfCellIs1 = (areWeFlipping ? 1 : 0) + solve(r, c + 1, newMc, newMb, newP); // Continue the search

ref = min( nAdditionalFlipsIfCellIs0, nAdditionalFlipsIfCellIs1 );
/* Try not flipping the current cell */
int nAdditionalFlipsIfDontFlip = 0 + solve(r, c + 1, mc, mb, p);

/* Try flipping it */
int newMc = mc ^ (1 << c);
int newMb = mb ^ (1 << (c / 3));
int newP = p ^ 1;
int nAdditionalFlipsIfFlip = 1 + solve(r, c + 1, newMc, newMb, newP);

ref = min( nAdditionalFlipsIfDontFlip, nAdditionalFlipsIfFlip );