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

相关文章

关于C++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

IPython小白教程:提升你的Python交互式编程技巧,通俗易懂!

IPython是一个增强的Python交互式shell,它提供了丰富的功能和便捷的交互方式,使得Python开发和数据分析工作更加高效。本文将详细介绍IPython的基本概念、使用方法、主要作用以及注意事项。 一、IPython简介 1. IPython的起源 IPython由Fernando Pérez于2001年创建,旨在提供一个更高效的Python交互式编程环境。 2. IPyt

从《深入设计模式》一书中学到的编程智慧

软件设计原则   优秀设计的特征   在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。 代码复用 无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对手更早进入市场; 较低的开发成本意味着能够留出更多营销资金,因此能更广泛地覆盖潜在客户。 代码复用是减少开发成本时最常用的方式之一。其意图

Java并发编程—阻塞队列源码分析

在前面几篇文章中,我们讨论了同步容器(Hashtable、Vector),也讨论了并发容器(ConcurrentHashMap、CopyOnWriteArrayList),这些工具都为我们编写多线程程序提供了很大的方便。今天我们来讨论另外一类容器:阻塞队列。   在前面我们接触的队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了D

剑指offer—编程题7(用两个栈实现一个队列)

题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail 和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能。 代码如下: [java]  view plain copy print ? public class Test07 {       /**       * 用两个栈模拟的队列       *

剑指Offer—编程题4 ( 替换空格)

一、题目:替换空格 题目:请实现一个函数,把字符串中的每个空格替换成"%20"。例如输入“We are happy.”,则输出“We%20are%20happy.”。    在网络编程中,如果URL参数中含有特殊字符,如空格、'#'等,可能导致服务器端无法获得正确的参数值。我们需要将这些特殊符号转换成服务器可以识别的字符。转换的规则是在'%'后面跟上ASCII码的两位十六进制的表示。

剑指Offer—编程题56(链表中环的入口地址)

题目:一个链表中包含环,如何找出环的入口结点? 解题思路   可以用两个指针来解决这个问题。先定义两个指针P1和P2指向链表的头结点。如果链表中环有n个结点,指针P1在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口结点时,第一个指针已经围绕着环走了一圈又回到了入口结点。    剩下的问题就是如何得到环中结点的数目。我们在面试题15的第二个相关题目时用到