• Mondriaan's Dream题解


    题目

    求用1*2的牌铺满n*m大小的棋盘的方案数。

    思路

    关键是设计牌状态,尤其是竖着的。

    如果令竖着的骨牌上面为1。

    这样两个状态不冲突的条件是某一位不能同时为1且或运算后不能有连续奇数个0。

    最终第n行应该是全0。

    考虑第一行, 只要其不含奇数个0, 就合法,为了方便, 设计第0行的0方案数为1, 这样按照策略转移第一行的全部合法状态都会变成1. 当然也可以特殊处理第一行。

    最终答案为f[n][0];

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    
    using namespace std;
    
    int n, m;
    long long f[12][1<<11];
    int valid[1<<11];
    
    int check(int S) {
    	int k = 0;
    	while(k < m) {
    		if(S&1) S >>= 1, k++;
    		else if(!(S&1) && !(S&2) && k+2 <= m) S >>= 2, k += 2;
    		else return 1;
    	}
    	return 0;
    }
    
    int main() {
    	while(scanf("%d%d", &n, &m) && n && m) {
    		memset(f, 0, sizeof(f));
    		memset(valid, 0, sizeof(valid));
    		
    		for(int S = 0; S < (1<<m); S++) {
    			if(check(S)) continue;
    			valid[S] = 1;
    		}
    		
    		//We can also deal the first line and start at i = 2
    //		for(int S = 0; S < (1<<m); S++) {
    //			if(check(S)) continue;
    //			valid[S] = 1;
    //			f[1][S] = 1;
    //		}
    		
    		
    		f[0][0] = 1;
    		int now = 1;
    		for(int i = 1; i <= n; i++, now ^= 1) {
    			memset(f[now], 0, sizeof(f[now]));
    			for(int v = 0; v < (1<<m); v++) {
    				for(int u = 0; u < (1<<m); u++) {
    					if((u&v) || !valid[u|v]) continue;
    					f[now][v] += f[now^1][u];
    				}
    			}
    		}
    
    		
    		cout << f[now^1][0] << '
    ';
    	}
    	return 0;
    }
    

    如果令竖着的骨牌下面为1. 状态冲突条件不变。

    考虑第一行只能全为0, 设计其方案数为1. 这样第二行合法的便全部可以转移呈1.

    处理完后, 最后一行需要遍历一下所有状态, 只要不含奇数个0, 就合法,累加到答案中。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    
    using namespace std;
    
    int n, m;
    long long f[12][1<<11];
    int valid[1<<11];
    
    int check(int S) {
    	int k = 0;
    	while(k < m) {
    		if(S&1) S >>= 1, k++;
    		else if(!(S&1) && !(S&2) && k+2 <= m) S >>= 2, k += 2;
    		else return 1;
    	}
    	return 0;
    }
    
    int main() {
    	while(scanf("%d%d", &n, &m) && n && m) {
    		memset(f, 0, sizeof(f));
    		memset(valid, 0, sizeof(valid));
    		
    		for(int S = 0; S < (1<<m); S++) {
    			if(check(S)) continue;
    			valid[S] = 1;
    		}
    		
    		f[1][0] = 1;
    		int now = 0;
    		for(int i = 2; i <= n; i++, now ^= 1) {
    			memset(f[now], 0, sizeof(f[now]));
    			for(int v = 0; v < (1<<m); v++) {
    				for(int u = 0; u < (1<<m); u++) {
    					if((u&v) || !valid[u|v]) continue;
    					f[now][v] += f[now^1][u];
    				}
    			}
    		}
    
    		long long ans = 0;
    		for(int S = 0; S < (1<<m); S++) if(valid[S]) ans += f[now^1][S]; 
    		cout << ans << '
    ';
    	}
    	return 0;
    }
    

    关键

    竖着的骨牌的处理不同, 决策不同。

    动态规划状态的设计和转移的策略。 需要考虑完备。

    预处理合法状态。

  • 相关阅读:
    javascript无提示关闭窗口,兼容IE,Firefox
    vbs简单制作U灵大盗带发送功能的代码
    正则表达式提取网址、标题、图片等一例(.Net Asp Javascript/Js)的实现
    在asp.net中保持Session的有效期
    批处理编程 介绍
    IIS日志清理CMD版,VBS版,JS版,WSH版
    正则表达式提取图片地址
    用U盘安装GNU/Linux
    三核浏览器Lunascape新版发布
    黑客基础之DOS(最齐全)
  • 原文地址:https://www.cnblogs.com/Maktub-blog/p/15064277.html
Copyright © 2020-2023  润新知