poj 2411 编程之美 4.2 瓷砖覆盖地板

2024-03-17 15:38

本文主要是介绍poj 2411 编程之美 4.2 瓷砖覆盖地板,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意:用 1 * 2 的瓷砖覆盖 n * m 的地板,问共有多少种覆盖方式? 

思路:用2进制的01表示不放还是放,第i行只和i-1行有关,枚举i-1行的每个状态,推出由此状态能达到的i行状态:如果i-1行的出发状态某处未放,必然要在i行放一个竖的方块,所以我对上一行状态按位取反之后的状态就是放置了竖方块的状态。

然后用dfs搜索在i行放横着的方块的所有可能,并且把这些状态累加上i-1的出发状态的方法数,如果该方法数为0,直接continue。

 

#include <stdio.h>  
#include <string.h>  /** n * m 的地板 */  
int n,m;  /** dp[i][j] = x 表示使第i 行状态为j 的方法总数为x */  
__int64 dp[12][2049];  /* 该方法用于搜索某一行的横向放置瓷砖的状态数,并把这些状态累加上row-1 行的出发状态的方法数 * @name row 行数  * @name state 由上一行决定的这一行必须放置竖向瓷砖的地方,s的二进制表示中的1 就是这些地方 * @name pos 列数 * @name pre_num row-1 行的出发状态为~s 的方法数 */   
void dfs( int row, int state, int pos, __int64 pre_num )  
{  /** 到最后一列  */  if( pos == m ){  dp[row][state] += pre_num;  return;  }  /** 该列不放 */  dfs( row, state, pos + 1, pre_num );  /** 该列和下一列放置一块横向的瓷砖 */  if( ( pos <= m-2 ) && !( state & ( 1 << pos ) ) && !( state & ( 1 << ( pos + 1 ) ) ) )  dfs( row, state | ( 1 << pos ) | ( 1 << ( pos + 1 ) ), pos + 2, pre_num );  
}  
int main()  
{     while( scanf("%d%d",&n,&m) && ( n || m ) ){  /** 对较小的数进行状压,已提高效率 */  if( n < m ){  n=n^m;  m=n^m;  n=n^m;  }  memset( dp, 0, sizeof( dp ) );  /** 初始化第一行 */  dfs( 1, 0, 0, 1 );  for( int i = 2; i <= n; i ++ )   for( int j = 0; j < ( 1 << m ); j ++ ){  if( dp[i-1][j] ){  __int64 tmp = dp[i-1][j];  /* 如果i-1行的出发状态某处未放,必然要在i行放一个竖的方块, * 所以我对上一行状态按位取反之后的状态就是放置了竖方块的状态 */  dfs( i, ( ~j ) & ( ( 1 << m ) - 1 ), 0, tmp ) ;  }  else continue;        }  /** 注意并不是循环i 输出 dp[n][i]中的最大值 */  printf( "%I64d\n",dp[n][(1<<m)-1] );   }  return 0;  
}  

Python Version:


def dfs(row, state, pos, prenum, maxcol, dp):#row: the current row#state: the state of the current line#pos: the pos of the current row#prenum: the count of dp[row-1][~state]if (pos == maxcol):dp[row][state] += prenumreturn#3 situations:#1. If the col in the previous row is not filled, it won't be considered in the dfs codes since we don't store of the previous line in dfs codes#2. No matter whether the col is filled, move to the nextdfs(row, state, pos+1, prenum, maxcol, dp)#3. Fill two consecutive colsif ((pos+1 <= maxcol-1) and (state & (1 << pos) == 0) and  (state & (1 << (pos+1)) == 0) ):dfs(row, state | (1 << pos) | (1 << (pos+1)), pos+2, prenum, maxcol, dp)def cal(m, n):if (m > n):m, n = n, mdp = [[0 for i in range(0, (1<<m)+1)] for j in range(0, n+1)]dfs(1, 0, 0, 1, m, dp)for i in range(2, n+1):for j in range(0, 1 << m):if (dp[i-1][j] != 0):#Consider situation 1 here:dfs(i, (~j) & ((1<<m)-1), 0, dp[i-1][j], m, dp)return dp[n][(1<<m)-1]if __name__ == '__main__':res = cal(7,8)print(res)

扩展:

用p*q的瓷砖能覆盖M*N的地板的充要条件是:(这是一个判定问题,并不需要求方法数)
1。第一行和第一列可以被覆盖
2。m或n可以被p整除并且m或n可以被q整除

简单证明:

①当m(或n)被p 整除 & n(或m)被q 整除时,易知,一定能覆盖(一行行覆盖)

②当m(或n)被p * q 整除时,只要第一行的n 个格子能被覆盖,则一定能覆盖

③当①与②都不满足时,根据面积易知一定不能覆盖

也可以用反证法:

假设m*n被p*q的瓷砖铺满,那么一定

m= ap+bq

n=cp+dq

mn=acp^2+adpq+bcpg+bdq^2

mn=epq

假设a,b,c,d,e都不是0,而且a,b,c,d,p,q都是质数,那么acp=fq, bdq=gp, 但是这样的f,g是不存在的,所以a,c之一必定是0,b,d之一必定是0

 

更详细的参见下面的slides:
http://www-math.mit.edu/~rstan/transparencies/tilings.pdf

这篇关于poj 2411 编程之美 4.2 瓷砖覆盖地板的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/819376

相关文章

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1502 MPI Maelstrom(单源最短路dijkstra)

题目真是长得头疼,好多生词,给跪。 没啥好说的,英语大水逼。 借助字典尝试翻译了一下,水逼直译求不喷 Description: BIT他们的超级计算机最近交货了。(定语秀了一堆词汇那就省略吧再见) Valentine McKee的研究顾问Jack Swigert,要她来测试一下这个系统。 Valentine告诉Swigert:“因为阿波罗是一个分布式共享内存的机器,所以它的内存访问