欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

uva1602

程序员文章站 2022-06-02 11:54:00
...

uva1602

题意

链接如下:https://vjudge.net/problem/UVA-1602
中文解释:
输入n,w,h(1<=n<=10,1<=w,h<=n).求能放在w*h网格里的不同的n连块的个数(平移,旋转,翻转算一种)
例如:第一排为n=5,w=2,h=4
第2牌为n=8,w=3,h=3
uva1602

题解

很直接的思路:打表+STL(set)
重难点是 如何判断重复
因为可以平移,旋转,翻转,所以我们需要给每一个连通块一个标准化(让其的相对起始点都在(0,0))。
利用set实现判重
本题可以先把所有情况都列举出来(打表)。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>

using namespace std;
int n, w, h;
const int N = 10;
struct cell
{
    int r, c;
    cell(int r = 0, int c = 0)
    {
        this->r = r;
        this->c = c;
    }
    bool operator<(const cell rhs)const
    {
        return r < rhs.r || (r == rhs.r && c < rhs.c);
    }
};
typedef set<cell> block;
set<block> s[N + 1];
block normalize(block b)
{
    // 标准化联通块
    int minr = 200, minc = 200;
    for (block::iterator iter = b.begin(); iter != b.end(); iter++)
    {
        minr = min(minr, iter->r);
        minc = min(minc, iter->c);
    }
    block b2;
    for (block::iterator iter = b.begin(); iter != b.end(); iter++)
    {
        b2.insert(cell(iter->r - minr, iter->c - minc));
    }
    return b2;
}
block rotate(block b)
{
    // 顺时针旋转90°
    block ret;
    for (block::iterator iter = b.begin(); iter != b.end(); iter++)
    {
        // 遍历每个元素
        ret.insert(cell(iter->c, (iter->r) * -1));
    }
    return normalize(ret);
}
int ans[N + 1][N + 1][N + 1];
int dr[] = {-1, 1, 0, 0};
int dc[] = {0, 0, -1, 1};
block flip(block b)
{
    // 翻转联通块
    block b2;
    for (block::iterator iter = b.begin(); iter != b.end(); iter++)
    {
        b2.insert(cell(iter->r, -iter->c)); // 沿着x轴翻转
        // 只需要沿着x轴翻转,因为在rotate中会旋转一次
    }
    return normalize(b2);
}
void checkBlock(block b0, cell c)
{
    // 检查当前联通块是否可以加入该新方格
    block b = b0;
    b.insert(c);
    // 标准化联通块
    b = normalize(b);

    int cntBlockSize = b.size();
    for (int i = 0; i < 4; i++)
    {
        if (s[cntBlockSize].count(b))
            return;
        b = rotate(b);
    }
    b = flip(b);
    for (int i = 0; i < 4; i++)
    {
        if (s[cntBlockSize].count(b))
            return;
        b = rotate(b);
    }
    s[cntBlockSize].insert(b);
}
int maxn = 10;
void solve()
{
    // 打表所有情况
    block a;
    a.insert(cell(0, 0));
    s[1].insert(a);
    for (int i = 2; i <= maxn; i++)
    {
        // 遍历每种个数的联通块
        for (set<block>::iterator iter = s[i - 1].begin(); iter != s[i - 1].end(); iter++)
        {
            // 枚举每个连通块
            for (block::iterator citer = (*iter).begin(); citer != (*iter).end(); citer++)
            {
                // 枚举每个联通块的每个单元格
                for (int dir = 0; dir < 4; dir++)
                {
                    // 枚举4个方向
                    cell newc(citer->r + dr[dir], citer->c + dc[dir]);
                    if (iter->count(newc) == 0)
                    {
                        // if判断是否存在该单元格
                        checkBlock(*iter, newc);
                    }
                }
            }
        }
    }
    // 开始打表,枚举每一种情况 
    for (int nn = 1; nn <= maxn; nn++)
    {
        for (int ww = 1; ww <= maxn; ww++)
        {
            for (int hh = 1; hh <= maxn; hh++)
            {
                int cnt = 0;
                for (set<block>::iterator iter = s[nn].begin(); iter != s[nn].end(); iter++)
                {
                    int maxr = 0, maxc = 0;
                    for (block::iterator citer = (*iter).begin(); citer != (*iter).end(); citer++)
                    {
                        maxr = max(maxr, citer->r);
                        maxc = max(maxc, citer->c);
                    }
                    if (min(maxr, maxc) < min(hh, ww) && max(maxr, maxc) < max(hh, ww))
                        cnt++;
                }
                ans[nn][ww][hh] = cnt;
            }
        }
    }
}
int main()
{
    solve(); // vjudge需要400ms
    while (scanf("%d%d%d", &n, &w, &h) != EOF)
    {
        printf("%d\n", ans[n][w][h]);
    }
    return 0;
}

推荐阅读