暑期多校联合第一场 H题 俄罗斯方块 (整理暑期题目系列)

本文探讨了使用特定数量的俄罗斯方块拼成指定尺寸矩形的不同组合方式,包括方块旋转与排列的多种可能性。

                                               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); 
  }
}
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值