POJ 2411 解题报告

来源:互联网 发布:c语言define怎么用 编辑:程序博客网 时间:2024/06/07 20:53

这道题是状态压缩DP。之前应该做过一次类似的,但这道题比较难些。花了比较久理解状态压缩DP。周伟的论文《状态压缩》是最好的资料。

这道题是论文中的一道例题。论文中提到,由于合法状态特别多,所以这里对每一行都跑一次DFS,而不是提前保存下来。这样可以节省空间,时间上也没多少影响。事实也确实如此,16ms就过了。

整体过程是从上到下一行行往下填,DP[r][s]表示,第r行状态为s时填充方法有多少。我们要求的是DP[h - 1][(1 << w) - 1]的值,因为我们需要保证最后一行全都填充。

我们可以讲横着(horizontal)放置的小方块表示为11。即当前行的当前列和下一列填1。而把竖着(vertical)放置的小方块表示为01。即上一行的当前列为0,当前行的当前列为1。最后一行全都填充的意思是最后一行的每一列都不能是竖条的起点(0),即必须全为1,所以我们有(1<<w) - 1。

当前行的状态只和前一行的状态有关,反过来讲,如果当前行的状态确定,上一行的状态也确定了。反过来想比较容易填充 DP[r][s]。

对每一行,我们从左到右一列列填充。具体来说,

1.如果我们在当前行r的列c和c+1放一个横条,那么上一行(r -1)的c和c +1也必须为1(一个横条或者两个竖条的终点,或者一半一半,总之必须为11)。即有:

dfs(r, c + 2, (s1 << 2) | 3, (s2 << 2) | 3);

2.如果我们将当前行r的列c作为竖条的终点,那么上一行(r -1)的当前列c一定是竖条的起点(0)。即有:

dfs(r, c + 1, (s1 << 1) | 1 , s2 << 1);

3.如果我们将当前行r的列c作为竖条的起点,那么上一行(r -1)的当前列c一定是已经填充好了,即必须为1(无论是横条还是竖条的终点,总之已经“完整”地填充了)。即有:

dfs(r, c + 1, s1 << 1 , (s2 << 1) | 1);

当前行的状态s1是所有上一行可能的状态s2的填充方法之和,即:dp[r][s1] += dp[r - 1][s2];

还有一个值得注意的地方是第一行(r = 0)。第一行不能作为竖条的终点,即不能存在上述情况2.并且,因为不存在前一行,dp[r][s1] = 1就好了,即标记本身是合法的就好。

thestoryofsnow2411Accepted308K16MSC++1901B

/* ID: thestor1 LANG: C++ TASK: poj2411 */#include <iostream>#include <fstream>#include <cmath>#include <cstdio>#include <cstring>#include <limits>#include <string>#include <vector>#include <list>#include <set>#include <map>#include <queue>#include <stack>#include <algorithm>#include <cassert>using namespace std;const int MAXH = 11;const int MAXW = 11;const int MAXS = 1 << MAXW;int h, w;long long dp[MAXH][MAXS];// horizontal:  1 1// vertical  :  0 1// the last row should be all 1// s1 is current row status, // s2 is previous(!!!) row status// according to Zhou Wei's paper, // the valid status is plenty// so we run it every row to save spacevoid dfs(int r, int c, int s1, int s2){if (c >= w){// does not cross borderif (c == w){if (r == 0){dp[r][s1] = 1;}else{dp[r][s1] += dp[r - 1][s2];}}return;}// if current row is horizontal, then the previous row should be horizontal as well// (or both columns are the end of vertical which are the same, '11')dfs(r, c + 2, (s1 << 2) | 3, (s2 << 2) | 3);// current row is the end of vertical, then the previous row should be the start// the first row cannot be the end of verticalif (r != 0){dfs(r, c + 1, (s1 << 1) | 1 , s2 << 1);}// current row is the start of vertical, // then the previous row should be the end// (or part of horizontal, in both cases, '1')dfs(r, c + 1, s1 << 1 , (s2 << 1) | 1);}int main(){while (scanf("%d%d", &h, &w) > 0 && h){// rotate the rectangle to make the width smallerif (h < w){int t = h;h = w;w = t;}for (int r = 0; r < h; ++r){for (int s = 0; s < (1 << w); ++s){dp[r][s] = 0;}}for (int r = 0; r < h; ++r){dfs(r, 0, 0, 0);}// the last row should be all 1printf("%lld\n", dp[h - 1][(1 << w) - 1]);}return 0;}




0 0
原创粉丝点击