• 多米诺牌集


    多米诺gu牌覆盖问题也是经典的题目了,主要是由1*k的牌覆盖m*n的矩阵之类的。有递推求解的,也有的如下:

    如POJ 2411,由1*2的小矩形去覆盖m*n的矩形,问有多少种方案。

    这道题其实只要计算出上下两行之前的可能存在的状态转换,这样就很容易求解了。如下:

    void dfs(int pre,int now,int l){//上一行的状态,当前行的状态,当前列的位置
    	if(l>w) return;
    	if(l==w){
    		trans[nt][0]=pre,trans[nt++][1]=now;
    		return ;
    	}
    	dfs(pre<<1,now<<1|1,l+1); //竖着放,则上一行当前为置为空,下一行被覆盖   
    	dfs(pre<<2|3,now<<2|3,l+2);//当前行横着放
    	dfs(pre<<1|1,now<<1,l+1);//当前行当前位置不放
    }
    

      

    于是,POJ 2411 可以如下解题:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define LL __int64
    using namespace std;
    int nt,h,w;
    int trans[11*(1<<11)][2];
    LL f[12][1<<11];
    
    
    void dfs(int pre,int now,int l){
    	if(l>w) return;
    	if(l==w){
    		trans[nt][0]=pre,trans[nt++][1]=now;
    		return ;
    	}
    	dfs(pre<<1,now<<1|1,l+1);
    	dfs(pre<<2|3,now<<2|3,l+2);
    	dfs(pre<<1|1,now<<1,l+1);
    }
    
    int main(){
    	while(scanf("%d%d",&h,&w),h||w){
    		nt=0;
    		dfs(0,0,0);
    		memset(f,0,sizeof(f));
    		f[0][(1<<w)-1]=1;
    		for(int i=1;i<=h;i++){
    			for(int j=0;j<nt;j++){
    				if(f[i-1][trans[j][0]]){
    					f[i][trans[j][1]]+=f[i-1][trans[j][0]];
    				}
    			}
    		}
    		printf("%I64d
    ",f[h][(1<<w)-1]);	
    	}
    	return 0;
    }
    

      

    另外如POJ 2663,也是同样的解法。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define LL __int64
    using namespace std;
    
    int trans[10*(1<<3)][2];
    int nt;
    //LL dp[2001][20001];
    LL f[31][1<<3];
    
    void dfs(int pre,int now,int l){
        if(l>3) return ;
        if(l==3){
            trans[nt][0]=pre;
            trans[nt++][1]=now;
            return ;
        }
        dfs(pre<<1,now<<1|1,l+1);
        dfs(pre<<2|3,now<<2|3,l+2);
        dfs(pre<<1|1,now<<1,l+1);
    }
    
    int main(){
        int n;
        nt=0;
        dfs(0,0,0);
        while(~scanf("%d",&n)){
            if(n==-1) break;
            memset(f,0,sizeof(f));
            f[0][(1<<3)-1]=1;
            for(int i=1;i<=n;i++){
                for(int j=0;j<nt;j++){
                    if(f[i-1][trans[j][0]]){
                        f[i][trans[j][1]]+=f[i-1][trans[j][0]];
                    }
                }
            }
            printf("%I64d
    ",f[n][(1<<3)-1]);
        }
        return 0;
    }

    POJ 3420.由于n比较大,而且两行之间的状态转换就是不断的重复,可以使用快速矩阵乘法,把状态转换矩阵保存下来,然后快速乘,则结果保存在trans[15][15]的位置。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    struct Matrix{
        int trans[1<<4][1<<4];
        void init(){
            memset(trans,0,sizeof(trans));
        }
        void per(){
            memset(trans,0,sizeof(trans));
            for(int i=0;i<16;i++)
            trans[i][i]=1;
        }
    };
    int M,n;
    Matrix T;
    
    Matrix multi(Matrix a,Matrix b){
        Matrix ans;
        ans.init();
        for(int i=0;i<16;i++){
            for(int j=0;j<16;j++){
                for(int k=0;k<16;k++){
                    ans.trans[i][j]=(ans.trans[i][j]+(a.trans[i][k]*b.trans[k][j])%M)%M;
                }
            }
        }
        return ans;
    }
    
    void dfs(int pre,int now,int l){
        if(l>4) return ;
        if(l==4){
            T.trans[pre][now]++;
            return ;
        }
        dfs(pre<<1|1,now<<1,l+1);
        dfs(pre<<2|3,now<<2|3,l+2);
        dfs(pre<<1,now<<1|1,l+1);
    }
    
    int quick(int n){
        Matrix p; p.per();
        Matrix sq=T;
        while(n){
            if(n&1) p=multi(sq,p);
            sq=multi(sq,sq);
            n>>=1;
        }
        return p.trans[15][15];
    }
    
    int main(){
        T.init();
        dfs(0,0,0);
        while(scanf("%d%d",&n,&M),n||M){
            printf("%d
    ",quick(n));
        }
        return 0;
    }

    HDU 5100

    问以1*k的小矩阵覆盖n*n的大矩形,最多能覆盖的面积。

    对于n%k==0的情况就不用说了,对于不整除的,可以直观的发现,不覆盖的情况必定是个正方形,其边长要么是n%k或者k-n%k。其实这个是可以证明的,

    转自http://www.matrix67.com/blog/archives/5900。。。。。。我没有侵权啊。。。T_T

    用 k × 1 的小矩形覆盖一个 n × n 的正方形棋盘,往往不能实现完全覆盖(比如,有时候 n × n 甚至根本就不是 k 的整倍数)。不过,在众多覆盖方案中,总有一种覆盖方案会让没有覆盖到的方格个数达到最少,我们就用 m(n, k) 来表示这个数目。求证:不管 n 和 k 是多少, m(n, k) 一定是一个完全平方数。

    如果 n < k ,那么很明显,棋盘里一个小矩形也放不下,因而 m(n, k) = n2 ,这是一个完全平方数。下面我们就只考虑 n ≥ k 了。

    我们先来证明这样一个事实:如果某个覆盖方案当中,仅剩下一个 s × s 的小正方形区域没有覆盖到,其中 s ≤ k / 2 ,那么这样的方案一定是最优的。首先,在棋盘中的每个格子里都填上一个数,使得从最左下角出发,各个对角线上的数依次为 0, 1, 2, …, k – 1, 0, 1, 2, …, k – 1, … (上图所示的是 k = 6 的情况)。那么,把一个 k × 1 的小矩形放在棋盘中的任意位置,它总会覆盖每种数字各一个。假设某个覆盖方案当中,仅剩下一个 s × s 的小正方形区域没有覆盖到。注意到,任意一个 s × s 的小正方形区域里最多只会出现 2s – 1 种不同的数,因此如果 s ≤ k / 2 ,那么这个 s × s 的小正方形区域里一定会缺失至少一种数,比方说 x (在上图中,右上角的那个 3 × 3 的空白区域里就缺数字 5 ,因而我们可以取 x = 5 )。由此可以推出,此时小矩形的数目已经达到了最大值,任何其他覆盖方案都不可能包含更多的小矩形,因为每个小矩形都必然会覆盖到一个 x ,然而在刚才的覆盖方案中,所有的 x 都已经被覆盖到了。

    有趣的是,当 n ≥ k 时,让整个棋盘仅剩一个边长不超过 k / 2 的小正方形区域没有覆盖到,这是一定能做到的。不妨把 n 除以 k 的余数记作 r 。如果 r ≤ k / 2 ,那么我们可以直接用横着的小矩形从左向右填充棋盘,再用竖着的小矩形填充余下的部分,最终会剩下 r × r 的小正方形区域。上图所示的就是 n = 22 并且 k = 5 的情况,注意到 22 除以 5 的余数为 2 ,确实小于等于除数 5 的一半。可见,对于这类情况,我们都有 m(n, k) = r2 ,这是一个完全平方数。

    如果 r > k / 2 呢?我们可以用和刚才类似的方法填充棋盘,使得棋盘右上角仅剩一个 (r + k) × (r + k) 的正方形区域。然后再用 4r 个小矩形像风车一样填充这个 (r + k) × (r + k) 的区域,使得正中间只剩下一个边长为 k – r 的小正方形区域。由于 k – r < k / 2 ,因而此时的覆盖方案再次达到最优。上图所示的就是 n = 22 并且 k = 6 的情况,注意到 22 除以 6 的余数为 4 ,确实大于除数 6 的一半。可见,对于这类情况,我们有 m(n, k) = (k – r)2 ,这仍然是一个完全平方数。

    HDU 5100

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    int main(){
        int n,k,T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&k);
            if(k>n){
                puts("0");
                continue;
            }
            if(n%k==0)
            printf("%d
    ",n*n);
            else{
                int l=min(n%k,k-n%k);
                printf("%d
    ",n*n-l*l);
            }
        }
        return 0;
    }
  • 相关阅读:
    OpenCASCADE DataExchange DWG
    OpenCASCADE Conic to BSpline Curves-Circle
    OpenCASCADE Conic to BSpline Curves-Hyperbola
    Java中通过命令行启动jar包时指定编码
    VSCode来绘制流程图真是得心应手
    若依微服务版新建业务模块后提示找不到mapper的解决方法
    若依微服务版怎样实现不同业务模块下实体和mapper互相调用
    GitLab怎样实现新建仓库并允许开发者推送代码实现协同开发
    RuoYi-Process多模块activity工作流项目快速搭建
    若依微服务版手把手教你本地搭建环境并运行前后端项目
  • 原文地址:https://www.cnblogs.com/jie-dcai/p/4439389.html
Copyright © 2020-2023  润新知