题意:输入n、 w、 h(1≤n≤10,1≤w,h≤n),求能放在w*h网格里的不同的n连块的个数(注意,平移、 旋转、 翻转后相同的算作同一种)。
思路:此题数据结构太强大,自己根本敲不出来。。。抄了一遍代码仓库!
(1)枚举:枚举n个连通块,从1开始,四个方向进行添加块,生成新的连通块后进行判重,不重复加入n连块的集合中。
(2)判重:利用rotate()函数进行旋转4次连通块,如果没有重复,再利用flip()函数翻转后,再次旋转4次,如果没有重复说明是新的连通块,否则不是。
旋转:rotate()函数:将集合(即连通块)中的坐标全部以(y,-x)重新插入,即旋转了90度,然后再利用normalize()函数统一平移到标准位置。
翻转:flip()函数:进集合(连通块)中的坐标全部以(x,-y)重新插入,即翻转了连通块,然后再利用normalize()函数标准化。
平移:normalize()函数:以集合(连通块)中的最小坐标为基准,将所有点都减去最小点后即平移到了以最小点(0,0)为标准的位置。
(3)打表:利用3重循环,判断最大点中的较小点在(h,w)范围内,较大点也在(h,w)范围内后ans[n][w][h]++计数。
(4)输出:表已打好,直接输出ans[n][w][h]即可!
参考:Hengjie Yang博客 + 紫书代码仓库
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
struct Cell{//连通块坐标
int x,y;
Cell(int x=0,int y=0):x(x),y(y){};
bool operator < (const Cell& rhs) const{
return x < rhs.x || (x == rhs.x && y < rhs.y);//按x递增,当x相等按y递增
}
};
typedef set<Cell> Polyomino;
#define FOR_CELL(c,p) for(Polyomino::const_iterator c = (p).begin(); c!=(p).end();++c)
inline Polyomino normalize(const Polyomino &p){//标准化:以最小坐标为0,0点,将其他坐标减去最小坐标,同时平移过来,即位置统一
int minX = p.begin()->x , minY = p.begin()->y;
FOR_CELL(c,p){//寻找最小的坐标
minX = min(minX,c->x);
minY = min(minY,c->y);
}
Polyomino p2;
FOR_CELL(c,p)//将所有点平移
p2.insert(Cell(c->x - minX,c->y - minY));
return p2;
}
inline Polyomino rotate(const Polyomino &p){//旋转90度
Polyomino p2;
FOR_CELL(c,p)
p2.insert(Cell(c->y,-c->x));
return normalize(p2);//进行标准化后返回
}
inline Polyomino flip(const Polyomino &p){//翻转
Polyomino p2;
FOR_CELL(c,p)
p2.insert(Cell(c->x,-c->y));
return normalize(p2);
}
const int dx[] = {-1,1,0,0};
const int dy[] = {0,0,-1,1};
const int maxn = 10;
set<Polyomino> poly[maxn+1];//存放n连块的个数
int ans[maxn+1][maxn+1][maxn+1];
void check_polyomino(const Polyomino& p0,const Cell& c){//加入新块的判重
Polyomino p = p0;
p.insert(c);//将新的块加入
p = normalize(p);//标准化处理
int n = p.size();//计算集合的长度用于存放n连块的数组下标
for(int i=0;i<4;i++){//旋转4次进行查重
if(poly[n].count(p) != 0) return;
p = rotate(p);
}
p = flip(p);//翻转
for(int i=0;i<4;i++){//翻转后再旋转4次查重
if(poly[n].count(p) != 0) return;
p = rotate(p);
}
poly[n].insert(p);//新块加入
}
void generate(){
Polyomino s;
s.insert(Cell(0,0));
poly[1].insert(s);//1连块只有1个块
for(int n=2;n <= maxn;n++){
for(set<Polyomino>::iterator p = poly[n-1].begin();p != poly[n-1].end();++p)//从上一个连通块开始进行加块枚举
FOR_CELL(c,*p)//将每一个块的4个方向进行加块
for(int dir = 0;dir < 4;dir++){
Cell newc(c->x + dx[dir],c->y + dy[dir]);//生成新块
if(p->count(newc) == 0) check_polyomino(*p,newc);//进行查重并插入
}
}
//打表,将矩阵范围内的连通块筛选出来
for(int n = 1;n <= maxn;n++)
for(int w = 1;w <= maxn;w++)
for(int h = 1;h <= maxn;h++){
int cnt = 0;
for(set<Polyomino>::iterator p = poly[n].begin(); p!= poly[n].end(); ++p){
int maxX = 0,maxY = 0;
FOR_CELL(c,*p){
maxX = max(maxX,c->x);
maxY = max(maxY,c->y);
}
if(min(maxX,maxY) < min(h,w) && max(maxX,maxY) < max(h,w)) ++cnt;//最大坐标在边界内可以累计连通块
}
ans[n][w][h] = cnt;//保存连通块值
}
}
int main()
{
generate();
int n,w,h;
while(scanf("%d%d%d",&n,&w,&h) == 3) printf("%d\n",ans[n][w][h]);
return 0;
}