Problem H. 俄罗斯方块
Time Limit: 6000ms Memory Limit: 65536kb
Description
现在给你一些俄罗斯方块游戏中的小方块,你需要求出用它们拼出一个N M 的矩形的方法有多少种。在
这里,方块不能重叠,方块不一定要用完,每种方块只能旋转0/90/180/270 度,不能翻转。方块有以下7 种,
已经编号。
1.
# # # #
2.
# #
# #
3.
#
# # #
4.
#
# # #
5.
#
# # #
6.
# #
# #
7.
# #
# #
我们认为两种方法不同,当且仅当存在一个格子,在两种方法中盖在这个格子上的方块不同。其中方块不
同是指种类不同,或者种类相同但形态不同(比如第一种方块转0 度和转90 度形态不同,但是转0 度和转
180 度是相同的)。
Input
输入包含多组数据,其中第一行一个整数T,表示数据组数。
对于每组数据,第一行两个整数N;M(1 N M 32),表示需要拼出来的矩形的大小。
第二行,包含7 个整数,依次表示编号从1 到7 这7 种种类的方块的个数。
Output
对于每组数据,输出一行,用一个整数表示不同的拼法有多少种。
Sample
INPUT OUTPUT
1
2 4
100 100 100 100 100 100 100
4
program(标称代码,也很猥琐):
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <algorithm>
#include <iomanip>
#include <map>
#include <set>
#include <vector>
using namespace std;
struct Point {
int x, y;
Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
};
inline Point operator+ (const Point &a, const Point &b) { return Point(a.x + b.x, a.y + b.y); }
inline Point operator- (const Point &a, const Point &b) { return Point(a.x - b.x, a.y - b.y); }
const Point block[19][4] = {
{Point(0, 0), Point(0, 1), Point(0, 2), Point(0, 3)},
{Point(0, 0), Point(1, 0), Point(2, 0), Point(3, 0)},
{Point(0, 0), Point(0, 1), Point(1, 0), Point(1, 1)},
{Point(0, 0), Point(1, 0), Point(1, 1), Point(1, 2)}, //y0
{Point(0, 0), Point(0, 1), Point(1, 0), Point(2, 0)}, //y0
{Point(0, 0), Point(0, 1), Point(0, 2), Point(1, 2)}, //y0
{Point(0, 1), Point(1, 1), Point(2, 0), Point(2, 1)}, //y0--
{Point(0, 2), Point(1, 0), Point(1, 1), Point(1, 2)}, //y1--
{Point(0, 0), Point(1, 0), Point(2, 0), Point(2, 1)}, //y1
{Point(0, 0), Point(0, 1), Point(0, 2), Point(1, 0)}, //y1
{Point(0, 0), Point(0, 1), Point(1, 1), Point(2, 1)}, //y1
{Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2)}, //y2
{Point(0, 0), Point(1, 0), Point(1, 1), Point(2, 0)}, //y2
{Point(0, 0), Point(0, 1), Point(0, 2), Point(1, 1)}, //y2
{Point(0, 1), Point(1, 0), Point(1, 1), Point(2, 1)}, //y2
{Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 2)}, //y3
{Point(0, 1), Point(1, 0), Point(1, 1), Point(2, 0)}, //y3
{Point(0, 1), Point(0, 2), Point(1, 0), Point(1, 1)}, //y4--
{Point(0, 0), Point(1, 0), Point(1, 1), Point(2, 1)}, //y4
};
const int belong[19] = {0, 0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6};
const int MAXN = 33;
int n, m, ans, cnt[7], a[MAXN][MAXN], task;
inline bool check(Point p) {
return p.x >= 0 && p.y >= 0 && p.x < n && p.y < m && a[p.x][p.y] == 0;
}
inline void select(Point p) { a[p.x][p.y] = 1 - a[p.x][p.y]; }
void dfs(int x, int y)
{
if (a[x][y] == 1)
dfs(x + (y == m - 1), (y + 1) % m); //巧妙的处理地图循环
if (x == n)
{
++ans;
return;
}
Point now(x, y);
for (int i = 0; i < 19; ++i)
if (cnt[belong[i]])
{
for (int j = 0; j < 4; ++j)// 对于每个点都有4种形态
{
bool err = false;
for (int k = 0; k < 4; ++k)
if (!check(now + block[i][k] - block[i][j])) //这里最难理解就是没想到还要以每个点为基点再转4次
{
err = true;
break;
}
if (err)
continue;
for (int k = 0; k < 4; ++k)
select(now + block[i][k] - block[i][j]);
--cnt[belong[i]];
dfs(x + (y == m - 1), (y + 1) % m);
++cnt[belong[i]]; //恢复,此三行都是
for (int k = 0; k < 4; ++k)
select(now + block[i][k] - block[i][j]);
}
}
}
int main() {
for (scanf("%d", &task); task--; )
{
scanf("%d %d", &n, &m);
for (int i = 0; i < 7; ++i)
scanf("%d", cnt + i);
if (n * m % 4)
puts("0");
else
{
ans = 0;
dfs(0, 0);
printf("%d\n", ans);
}
}
}
本文探讨了使用特定数量的俄罗斯方块拼成指定尺寸矩形的不同组合方式,包括方块旋转与排列的多种可能性。
4295

被折叠的 条评论
为什么被折叠?



