铺瓷砖问题 (状态压缩轮廓线动态规划) (二)

作者: 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;
}

 

基于轮廓线的动态规划方法将时间复杂度从N*4^{M}降低到N*M*2^{M}, 并且状态转移关系更加简单。

 

 

 

 

 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值