zoukankan      html  css  js  c++  java
  • UVa 1602 Lattice Animals (STL && 生成n连块 && 无方向形状判重)

    题意 : 给定一个 w * h 的 矩阵,在矩阵中找不同n个连通块的个数(旋转,翻转,平移算作一种)

    分析 : 这题的关键点有两个

    ① 生成n连块并且存储起来(因为题目是多测试用例,如果每一次都重新生成必将浪费很多时间)

    ② 判断是否生成了重复的n连块

    存储 :首先先确定用什么结构来存储n连块的信息才能更好的进行操作,n连块是多个二维坐标的集合,所以这里先定义结构体 Cell {int x, int y} 表示坐标,然后将其塞到一个集合里面set<Cell>,n连块可能有不同的多种,所以这些n连块又构成了一个集合,再塞到一个集合里面set< set<Cell> >表示不同的多个n连块,n的取值是1~10,所以我们需要存储从1到10这些n连块的信息,可以用数组实现set< set<Cell> > poly[maxn+1] (maxn = 10),这样便解决了n连块的存储问题,而且对于每一个用例,都不用再重复计算了。

    生成 :这里采用从1到10顺序生成n连块,对于当前n连块,可以从n-1连块拓展而来,假如现在要生产n连块,那可以取出某一n-1连块的每一个点向四个方向拓展,一旦拓展合法则进行判重,看看之前是否已经产生了形状一样的n连块

    判重 :对于判重,这里麻烦的是有三种操作,即一个n连块经过旋转、平移、翻转后是一样的。首先来解决平移,如果能让所有的n连块都能想办法固定到一个地方,那无论是平移还是其他两个操作的判重都会变得简单,这里可以这样做,假设set<(x, y)>这个是当前这个n连块的坐标集合,我们找出其中最小的x和y=>minX, minY作为平移矢量,set<(x, y)>里面所有的点和(minX, minY)做减法,那么就能将这个n连块固定到(0, 0)附近,而且这个操作不受(x, y)所在的象限影响,对于固定之后的n连块只要对其坐标进行相应的操作即可判断,可以发现旋转四次和翻转两次后是一样的,所以这样做4次旋转=>翻转=>4次旋转,每一次都能产生新的坐标集合,在set中count一下就可以知道是否重复,而且不能进行两次一样的翻转中间是旋转,因为这样的话,原本“固定”的位置就会发生改变,如果没有重复,则加入到集合里面即可。

    #include<bits/stdc++.h>
    #define ssit set< set<Cell> >::iterator
    #define sit set<Cell>::iterator
    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{
            if(x == rhs.x) return y < rhs.y;
            return x < rhs.x;
        }
    };
    int dx[] = {1, -1, 0, 0};
    int dy[] = {0, 0, 1, -1};
    const int maxn = 10 + 1;
    set< set<Cell> > poly[maxn];
    int ans[maxn][maxn][maxn];
    inline set<Cell> Standard(const set<Cell> & temp)
    {
        int minX = temp.begin()->x, minY = temp.begin()->y;
        sit it;
        for(it=temp.begin(); it!=temp.end(); it++){///找出最小的(x, y)
            minX = min(minX, it->x);
            minY = min(minY, it->y);
        }
        set<Cell> ret;
        for(it=temp.begin(); it!=temp.end(); it++){///(minX, minY)平移到(0, 0),图形也跟着平移
            ret.insert(Cell(it->x-minX, it->y-minY));
        }
        return ret;
    }
    
    inline set<Cell> Rotate(const set<Cell> & temp)
    {
        set<Cell> ret;
        sit it;
        for(it=temp.begin(); it!=temp.end(); it++){
            ret.insert(Cell(it->y, -it->x));///坐标的顺时针90°旋转是(x, y)=>(y, -x),逆时针则是(x, y)=>(-y, x)
        }
        return Standard(ret);///变化后当然也要固定一下
    }
    
    inline set<Cell> Flip(const set<Cell> & temp)
    {
        set<Cell> ret;
        sit it;
        for(it=temp.begin(); it!=temp.end(); it++){
            ret.insert(Cell(it->x, -it->y));///翻转(x, y)=>(y, x)
        }
        return Standard(ret);
    }
    
    inline void IF_add(const set<Cell>& s, const Cell& New)
    {
        set<Cell> temp = s;///定义临时变量将新点加入到刚刚传进来的n-1连块集合里面,构成一个n连块,接下来进行判重
        temp.insert(New);
        temp = Standard(temp);///先根据平移矢量将(minX, minY)固定到(0, 0)位置
        int n = temp.size();
        for(int i=0; i<4; i++){///先进行四次旋转
            if(poly[n].count(temp)) return ;///如果poly已经有了,则说明已经重复了
            temp = Rotate(temp);
        }
        temp = Flip(temp);///翻转
        for(int i=0; i<4; i++){///再来四次旋转
            if(poly[n].count(temp)) return ;
            temp = Rotate(temp);
        }
        poly[n].insert(temp);///这个n连块是不和之前重复的,插入到poly[n]集合里面
    }
    inline void Generate()
    {
        set<Cell> fir;
        fir.insert(Cell(0,0));
        poly[1].insert(fir);///对于n=1的n连块,只有一种,将其定为我们刚刚说的固定地点(0, 0),然后开始拓展
        for(int n=2; n<=10; n++){///顺序产生
            for(ssit it=poly[n-1].begin(); it!=poly[n-1].end(); it++){///取出每一个n-1连块
                for(sit it2=(*it).begin(); it2!=(*it).end(); it2++){///去除某一个n-1连块的某一个坐标点
                    for(int i=0; i<4; i++){///将坐标点进行四个方向的拓展
                        int x = it2->x + dx[i];
                        int y = it2->y + dy[i];
                        Cell temp(x, y);
                        if(!it->count(temp)) IF_add(*it, temp);///如果拓展出来的点不和已有点重复则合法,进行判重处理
                    }
                }
            }
        }
    
        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(ssit it=poly[n].begin(); it!=poly[n].end(); it++){
                        int maxX = 0, maxY = 0;
                        for(sit it1=(*it).begin(); it1!=(*it).end(); it1++){
                            maxX = max(maxX, it1->x);
                            maxY = max(maxY, it1->y);
                        }
                        if(min(maxX, maxY) < min(h, w) && max(maxX, maxY) < max(h, w))///如果将这个形状的n连块最短和在h, w中最小的平行
                                                                                      ///最长的和h, w最长的平行,如果可行,则说明放得下
                            cnt++;
                    }
                    ans[n][w][h] = cnt;
                }
            }
        }
    
    }
    int main(void)
    {
        Generate();///预处理所有的n连块信息
        int h, w, n;
        while(~scanf("%d %d %d", &n, &w, &h)){
            if(n==1) {puts("1"); continue; }///特判断n=1的情况
            printf("%d
    ", ans[n][w][h]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    做技术选型时,要注意些什么?
    .net core 实现默认图片
    启用跨源请求 (CORS)
    使用 Cookie 而无需 ASP.NET 核心标识的身份验证
    Windbg+Procdump解决w3wp.exe CPU过百问题
    .NET Core 2.0 Cookie中间件 权限验证
    想要学习设计模式,你得先会看类图,一张图读懂UML
    ef core
    Dapper MySql DateTime 异常
    简单理解Socket
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7206869.html
Copyright © 2011-2022 走看看