求把 N×MN×MN×M 的棋盘分割成若干个 1×21×21×2 的长方形,有多少种方案。
例如当 N=2,M=4N=2,M=4N=2,M=4 时,共有 555 种方案。当 N=2,M=3N=2,M=3N=2,M=3 时,共有 333 种方案。
如下图所示:
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数 N和MN 和 MN和M。
当输入用例 N=0,M=0N=0,M=0N=0,M=0 时,表示输入终止,且该用例无需处理。
输出格式
每个测试用例输出一个结果,每个结果占一行。
数据范围
1≤N,M≤111≤N,M≤111≤N,M≤11
输入样例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例:
1
0
1
2
3
5
144
51205
这题最最核心的一点是要想到:横着摆放方块的方案数 = 总摆放方块方案数。
由于1×21×21×2的方块可以横着或是竖着放,观察图给样例,如果把所有能横着放的方块都放入后,竖着放的方块位置也就固定了,只管往里塞,于是结论就是要找总方案数只需要找到可以横着摆放方块的方案数。
状态表示
格子的摆放引起状态 这个状态定义有点长,什么意思呢?以下图为例: 其中尤其要注意的是:第iii列的jjj这个状态本质上是由于第i−1i-1i−1列状态造成影响的,也就是说,jjj中为1的地方在i−1i-1i−1列的对应位置上是横放了方块之后捅到第 iii 列去的。 以此类推,如果定义i−1i-1i−1列上的状态是kkk,那么这个kkk是经过i−2i-2i−2列的方块放置影响到的,就像下图: 那么绿色的方块是在i−2i-2i−2列放置导致的,此时的 k=(00100)2k=(00100)_2k=(00100)2,注意≠11101≠11101=11101。 状态计算 由于动态规划是按列数从小到大(0到m−10到m-10到m−1)挨着枚举状态,因此第 iii 列的方案数 判断合法状态 很明显不是所有格子摆放状态都是合法的。 最终,整块拼图的方案数存在了 给定一张 nnn 个点的带权无向图,点从 0∼n−10∼n−10∼n−1 标号,求起点 000 到终点 n−1n−1n−1 的最短 Hamilton 路径。 Hamilton 路径的定义是从 000 到 n−1n−1n−1 不重不漏地经过每个点恰好一次。 输入格式 接下来 nnn 行每行 nnn 个整数,其中第 iii 行第 jjj 个整数表示点 iii 到 jjj 的距离(记为 a[i,j]a[i,j]a[i,j])。 对于任意的 x,y,zx,y,zx,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x]并且a[x,y]+a[y,z]≥a[x,z]a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]a[x,x]=0,a[x,y]=a[y,x]并且a[x,y]+a[y,z]≥a[x,z]。 输出格式 数据范围 输入样例: 输出样例: 这里有一篇写得很棒的解释👉 最短Hamilton路径(超详解)。 状态表示 状态计算 状态计算用到的算法就很类似于floydfloydfloyd算法,即f[i][j]:前i - 1列的摆放状态已处理好的前提下,在i - 1列横放的小方块伸到第i列的格子状态为j的方案数。
其中j由于是一个状态表示,所以要把它想成一个二进制数,它的范围是由nnn(一列有nnn行)决定的,即为了表示nnn个格子需要有 2n2^n2n 个状态,即j∈(0,1<
表示当i=2(i从0开始)i=2(i从0开始)i=2(i从0开始),j=(11001)2j = (11001)_2j=(11001)2时的状态,jjj中为111的地方表示该列的这个位置被占了。
f[i][j],由前一列的方案数f[i - 1][k]转移而来,即f[i][j] = Σ(f[i - 1][k])。
st[]数组存,如果状态 jjj 连续空着的格子数为偶数 则 st[j] = true。而状态转移时是要判断方块从i−2i - 2i−2列合在第i−1i - 1i−1列的方块共有多少个,可以“或”运算,即判断st[j | k]。i - 2列伸到i - 1列的小方格 和i - 1列放置的小方格不能重复,啥意思呢?看下图 
可以看到,此时蓝色的方块和红色的方块 “撞” 在了一起,显然非法的,必须得让jjj和kkk中的“1”岔开,这里判断用到一个小技巧,j & k == 0则意味着相同位置不会同时出现1。f[m][0],0表示(00...0)2(00...0)_2(00...0)2,它意味着mmm列不放小方格,前m−1m-1m−1列已经完全摆放好并且不伸出来的状态。那么意味着拼图完整。
Code
#include
最短Hamilton路径
第一行输入整数 nnn。
输出一个整数,表示最短 Hamilton 路径的长度。
1≤n≤201≤n≤201≤n≤20
0≤a[i,j]≤1070≤a[i,j]≤10^70≤a[i,j]≤1075
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
18
详细解释
偷个懒吧hh。f[i][j]: 所有从0走到j,经过的路径是i(二进制数)的最短距离。
举个例子:i = 11001、j = 4时,1表示走到,0表示没走到,那么状态i代表的路径就是0 —> 3 —> 4,那么f[0b11001][4]就代表从0走到4,路径是0 —> 3 —> 4的最短距离。
因此,答案就存在f[0b111...1][n - 1] = f[(1 << n) - 1][]n - 1里面。ii的走法下,暴力地枚举每一个点j作为终点的情况j作为终点的基础上,然后再暴力地枚举每一个点k(这个点也可以想象成floydfloydfloyd里的那个k),目的是把k视作一个中间点(从位置上准确地讲,k是0走到j之前到达的最后一个点)0 —> (一系列不包含终点 j 的点) —> k —> j路径来对f[i][j]进行松弛操作,也就是f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j])。其中i - (1 << j)对应的就是 “一系列不包含终点 jjj 的点”。
Code
#include
下一篇:形容辛苦的词语