Uva1602 Lattice Animals 【枚举打表+形状判重】【例题7-14】

题目:Lattice Animals

题意:输入nwh1≤n≤101≤whn),求能放在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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值