作者: Phill King
邮箱: phillking1982@163.com
原创文章,转载请注明出处。
在之前的文章 铺瓷砖问题(一)中介绍了状态压缩动态规划的方法,但是时间复杂度较高。在此介绍一下轮廓线动态规划算法,可以更快的解决问题。
- 定义状态
依然设行数为N,列数为M.
在前文我们用整行来定义状态,然后对相邻两行的状态进行转移。 在此我们定义以当前格结尾的M个格子为状态的范围。如下图所示:
- 状态转移
这样定义状态虽然数量增多了,但好处是状态转移关系非常的简单,只有三种可能。因此总的复杂度依然可以降低很多。
状态转移的三种情况见下图:
转换1: 上一个状态最高位为1, 当前格可以选择不放。
newState = (State<<1)^(1<<M); // 需要将最高位清零
转换2:上一个状态最高位为0, 必须竖放
newState = (State<<1)^1 // 最低位设置为1
转换3: 上一格状态最高位为1,且最低位为0, 可以横放。
newState = (State<<1)^(1<<M)^3 // 低两位设置为0
- 代码:
long long int getCoverWays(int rows, int cols){
// the size of area must be even.
if((rows*cols)%2 != 0){
return 0;
}
// make sure columns is smaller;
if(cols>rows){
swap(rows,cols);
}
const int STATE_MAX = 1<<cols;
vector<vector<long long int> > dp(2, vector<long long int>(STATE_MAX,0));
// assume the initial state is all 1
int cur = 0;
dp[cur][STATE_MAX-1] = 1;
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cur ^= 1;
std::fill(dp[cur].begin(), dp[cur].end(), 0);
for(int k=0; k<STATE_MAX; k++){
if(dp[1-cur][k] == 0) continue;
// upper grid == 1
if((k<<1)&STATE_MAX){
// don't place now, upper grid == 1; set current grid = 0;
dp[cur][(k<<1)^STATE_MAX] += dp[1-cur][k];
// place left to right, upper grid == 1,previous grid == 0; set current grid = 1
if(j && !(k&1)) {
dp[cur][(k<<1)^3^STATE_MAX] += dp[1-cur][k];
}
}
// place vertical from upper line, upper grid == 0; set current grid = 1
else if(i && !((k<<1)&STATE_MAX) ){
dp[cur][(k<<1)^1] += dp[1-cur][k];
}
}
}
}
return dp[cur][STATE_MAX-1];
}
int main() {
int n = 10;
int m = 8;
cout<<getCoverWays(n, m)<<endl;
return 0;
}
基于轮廓线的动态规划方法将时间复杂度从降低到, 并且状态转移关系更加简单。