多米诺牌集

多米诺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\n",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\n",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\n",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*n);
        else{
            int l=min(n%k,k-n%k);
            printf("%d\n",n*n-l*l);
        }
    }
    return 0;
}
时间: 2024-09-29 23:30:39

多米诺牌集的相关文章

洛谷 P1282 多米诺骨牌

题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2.每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置. 编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小. 对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0. 输入输出格式 输入格式: 输入文件的第一行是一个正

POJ 1135 Domino Effect(最短路 多米诺骨牌)

题意 题目描述: 你知道多米诺骨牌除了用来玩多米诺骨牌游戏外,还有其他用途吗?多米诺骨牌游戏:取一 些多米诺骨牌,竖着排成连续的一行,两张骨牌之间只有很短的空隙.如果排列得很好,当你推 倒第 1张骨牌,会使其他骨牌连续地倒下(这就是短语"多米诺效应"的由来). 然而当骨牌数量很少时,这种玩法就没多大意思了,所以一些人在 80 年代早期开创了另一个 极端的多米诺骨牌游戏:用上百万张不同颜色.不同材料的骨牌拼成一幅复杂的图案.他们开创 了一种流行的艺术.在这种骨牌游戏中,通常有多行骨牌同时

棋盘的多米诺覆盖:Dimer Lattice Model,Pfaff 多项式,Kasteleyn 定理

这次来介绍计数组合学里面一个经典的问题:Dimer Lattice Model.问题是这样的:一个有 64 个方格的国际象棋棋盘,有多少种不同的多米诺骨牌覆盖?这里的覆盖是指不重复不遗漏地盖住整个棋盘. 下图是一种可能的覆盖方式(图片来自 Wiki 百科): 这个问题的答案是 12988816,非常大的一个数字,绝对不是一个一个数出来的.1961 年德国物理学家 Kasteleyn 借助于线性代数中的一个结论首先解决了这个问题,我们接下来就介绍他的方法. ~~~~~~~~~~~~~~~~~~~~

鲁米诺反应

鲁米诺(luminol),又名发光氨.一种在犯罪现场检测肉眼无法观察到的血液,可以显现出极微量的血迹形态(潜血反应).化学名称为3-氨基苯二甲酰肼.常温下是一种黄色晶体或者米黄色粉末,是一种比较稳定的人工合成的有机化合物.化学式为C8H7N3O2,溶液显强酸性,对眼睛.皮肤.呼吸道有一定刺激作用.由于血红蛋白含有铁,而铁能催化过氧化氢的分解,让过氧化氢变成水和单氧,单氧再氧化鲁米诺让它发光.所以鲁米诺广泛应用于刑事侦查.生物工程.化学示踪等领域.

luogu P1282 多米诺骨牌

题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2.每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置. 编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小. 对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0. 输入输出格式 输入格式: 输入文件的第一行是一个正

多米诺(codevs 3052)

题目描述 Description 一个矩形可以划分成M*N个小正方形,其中有一些小正方形不能使用.一个多米诺骨牌占用两个相邻的小正方形.试问整个区域内最多可以不重叠地放多少个多米诺骨牌且不占用任何一个被标记为无法使用的小正方形. 输入描述 Input Description 第一行有两个用空格隔开的正整数M和N. 第二行有一个正整数K,表示共有K个小正方形不能使用.输入数据保证K<=M*N. 以下K行每行有两个用空格隔开的数X和Y,表示第X行的第Y个小正方形不能使用. 输出描述 Output D

poj 1717==洛谷P1282 多米诺骨牌

Dominoes Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6571   Accepted: 2178 Description A domino is a flat, thumbsized tile, the face of which is divided into two squares, each left blank or bearing from one to six dots. There is a ro

AOJ 589.多米诺

Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MBTotal Submission: 34   Submission Accepted: 12 Description 有一个n*m的网格,用一些2*1的多米诺去填充.有以下规则:1.每一个多米诺必须完整覆盖两个格子2.多米诺之间不能重叠3.多米诺不能超过网格边界4.摆放多米诺时可以旋转,但是不能斜着摆放问最多可以用多少个多米诺去覆盖网格. Input 一个整数

多米诺翻转

我们的问题从一张 $8\times8$ 的国际象棋棋盘开始.棋盘的一个铺砌 $T$,是指用若干$1\times2$ 的多米诺骨牌不重叠不遗漏地盖住棋盘的一种方法.显然在一个铺砌 $T$ 中,每个骨牌恰好盖住一对相邻的方格,因此总共需要 32 张骨牌. 任给一个棋盘的铺砌 $T$,考虑这样一种局部的操作:选择 $T$ 中一个 $2\times2$ 的正方形,它包含一对平行的多米诺骨牌,然后将这对骨牌翻转 90 度,即把一对水平并排放置的骨牌变成垂直并排放置的(或者反之).这样一个操作叫做翻转(fi