zoukankan      html  css  js  c++  java
  • 哈理工第八届程序设计竞赛同步赛(高年级)B题(铺砖问题)解题报告。

    题目描述:

    小乐乐想要给自己搭建一个积木城堡。

    积木城堡我们假设为n*m的平面矩形。

    小乐乐现在手里有1*2,2*1两种地砖。

    小乐乐想知道自己有多少种组合方案。


    输入描述:

    第一行输入整数n,m。(1<=n,m<=10)


    输出描述:

    输出组合方案数。


    样例输入

    2 3

    样例输出

    3


    解析:本题是动态规划中的状态压缩经典题型,铺砖问题的模板。想解此题需要考虑两个问题,如何表示当前状态,以及如何状态转移。

    以下思路参考自:《算法竞赛进阶指南》李煜东

    首要解决的问题是如何确定状态。我们可以观察得到,第 i 行的状态(砖块铺法)只与第 i - 1 行的状态有关,于是我们自然的想到以行为状态,向下递推。具体可行性证明如下:

    参考上图,可见标号1,2,3。假设我们所处位置为第 i 行第 j 列( i ,j ),那该位置上方块只可能有三种情况(横着放左右俩块一样,都看作1):

        1.如果我们处于1方块上,说明该方块已放置结束,对下一行没有影响,也就是说下一行不需要续接

        2.假设我们处于方块2上,那么我们知道下一行必须再续接一个方块,即( i + 1, j) 上需要再放一个方块来和方块2组成一个竖着放的砖,这时对下一行的状态有影响。

        3.处于方块3时,我们只能放一个方块来续接上一行的方块,即和方块2来组成一个竖着放的砖。此时对下一行无影响。

    综上所述,当且仅当当前方块为2时,才对下一行有影响,而我们假设每个砖块都以当前行为起点横着放或竖着放,所以我们只需用两个状态,分别表示该位置对下一行是否有影响。只需要两个状态,自然而然的想到状态压缩。我们假设有m位二进制数,第k位为1时表示第k列对下一行有影响,为0表示没有影响。至此状态表示已解决,接下来需要解决状态转移问题。

    设F[ i , j ]表示第 i 行的状态为 j 时,前 i 行所有铺法的总数。j 是用十进制整数记录 m 位二进制数。

    第 i - 1 行的状态 k 能转移到第i行的状态 j ,当且仅当:

        1.j & k = 0 。这保证了每个数字1下面必须是数字0,也就是竖着放的砖上半部分在下一行必须要有下半部分。

        2.j 和 k 执行按位或运算结果的二进制表示中,每一段连续的0都必须是偶数个。这个很好理解,2*1的砖横着放,不管放了多少,一定是2的倍数嘛。

    以上即为状态转移条件。

    代码示例:(优化1:用到的数组只有两行,故可以用滚动数组。优化2:提前计算出每种状态的连续0是否为偶数个,提高可读性和运行速度)

    #include<iostream>
    using namespace std;
    const int MAXN = 12;
    int dp[2][1<<MAXN];
    bool is_even[1<<MAXN];
    void init(int m){
    	for(int i = 0;i < 1<<m;i++){
    		int cnt = 0;		//当有偶数个0时cnt为0,否则为1。
    		int odd = 0; 		//当该状态所有连续0个数都为偶数时,odd为0,否则为1 
    		for(int j = 0;j < m;j++)
    			if(i >> j & 1)	odd |= cnt,cnt = 0;
    			else cnt ^= 1;
    		is_even[i] = odd | cnt;
    	}
    }
    int solve(int n,int m){
    	init(m);
    	dp[0][0] = 1;
    	int idx = 1;
    	for(int i = 1;i <= n; i++, idx ^= 1)
    		for(int j = 0;j < 1<<m ;j++){
    			dp[idx][j] = 0;
    			for(int k = 0;k < 1<<m ;k++)
    				if((j&k) == 0 && !is_even[j|k])
    					dp[idx][j] += dp[idx^1][k];
    		}
    	return dp[idx^1][0];
    }
    int main(){
    	int n,m;
    	cin >> n >> m;
    	cout << solve(n,m) << endl;
    	return 0;
    } 

     

  • 相关阅读:
    每周工作进度及工作量统计
    debug阶段工作期站立会议2(进度推进)
    new NABCD
    事后诸葛亮会议 (尸体解剖)
    debug阶段工作期站立会议1
    用户使用报告
    Scrum会议10(Beta版本) 补交
    历年学生作品点评
    关于词频统计的效能测试
    敏捷开发之Scrum站立会议
  • 原文地址:https://www.cnblogs.com/long98/p/10352160.html
Copyright © 2011-2022 走看看