|
|
package class_2021_12_5_week;
|
|
|
|
|
|
// 来自hulu
|
|
|
// 你只有1*1、1*2、1*3、1*4,四种规格的砖块
|
|
|
// 你想铺满n行m列的区域,规则如下:
|
|
|
// 1)不管那种规格的砖,都只能横着摆
|
|
|
// 比如1*3这种规格的砖,3长度是水平方向,1长度是竖直方向
|
|
|
// 2)会有很多方法铺满整个区域,整块区域哪怕有一点点不一样,就算不同的方法
|
|
|
// 3)区域内部(不算区域整体的4条边界),不能有任何砖块的边界线,是从上一直贯穿到下的直线
|
|
|
// 返回符合三条规则下,铺满n行m列的区域,有多少种不同的摆放方法
|
|
|
public class Code03_WaysToBuildWall {
|
|
|
|
|
|
public static long[] r = { 0, 1, 2, 4, 8 };
|
|
|
|
|
|
public static long ways(int n, int m) {
|
|
|
if (n <= 0 || m <= 1) {
|
|
|
return 1;
|
|
|
}
|
|
|
// len[i] = 一共有1行的情况下,列的长度为i的时候有几种摆法(所有,不分合法和非法)
|
|
|
long[] len = new long[m + 1];
|
|
|
for (int i = 1; i <= Math.min(m, 4); i++) {
|
|
|
len[i] = r[i];
|
|
|
}
|
|
|
for (int i = 5; i <= m; i++) {
|
|
|
len[i] = len[i - 1] + len[i - 2] + len[i - 3] + len[i - 4];
|
|
|
}
|
|
|
// any[i] = 一共有n行的情况下,列的长度为i的时候有几种摆法(所有,不分合法和非法)
|
|
|
long[] any = new long[m + 1];
|
|
|
for (int i = 1; i <= m; i++) {
|
|
|
// n * i的区域:总共的摆法!不区分合法、不合法
|
|
|
any[i] = power(len[i], n);
|
|
|
}
|
|
|
// solid[i] = 一共有n行的情况下,列的长度为i的时候有几种合法的摆法
|
|
|
long[] solid = new long[m + 1];
|
|
|
solid[1] = 1;
|
|
|
for (int i = 2; i <= m; i++) {
|
|
|
long invalid = 0;
|
|
|
// N * i
|
|
|
// 1) (N * 1 合法) * (N * (i-1) 总共)
|
|
|
// 2) (N * 2 合法) * (N * (i-2) 总共)
|
|
|
// 3) (N * 3 合法) * (N * (i-3) 总共)
|
|
|
//
|
|
|
// j) (N * j 合法) * (N * (i-j) 总共)
|
|
|
for (int j = 1; j < i; j++) {
|
|
|
invalid += solid[j] * any[i - j];
|
|
|
}
|
|
|
solid[i] = any[i] - invalid;
|
|
|
}
|
|
|
return solid[m];
|
|
|
}
|
|
|
|
|
|
public static long power(long base, int power) {
|
|
|
long ans = 1;
|
|
|
while (power != 0) {
|
|
|
if ((power & 1) != 0) {
|
|
|
ans *= base;
|
|
|
}
|
|
|
base *= base;
|
|
|
power >>= 1;
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
}
|