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
题解
很直接的思路:打表+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;
}
推荐阅读