UVa 1602 Lattice Animals

Lattice animal  is a set of connected sites on a lattice. Lattice animals on a square lattice are especially popular subject of study and are also known as  polyominoes . Polyomino is usually represented as a set of sidewise connected squares. Polyomino with  n  squares is called  n -polyomino. In this problem you are to find a number of distinct  free   n -polyominoes that fit into rectangle  w×h . Free polyominoes can be rotated and flipped over, so that their rotations and mirror images are considered to be the same. For example, there are 5 different pentominoes (5-polyominoes) that fit into  2×4  rectangle and 3 different octominoes (8-polyominoes) that fit into  3×3  rectangle.
\epsfbox{p3224.eps}

Input 

The input file contains several test cases, one per line. This line consists of 3 integer numbers  n w , and  h  (   1$ \le$n$ \le$10 1$ \le$wh$ \le$n ).

Output 

For each one of the test cases, write to the output file a single line with a integer number -- the number of distinct free  n -polyominoes that fit into rectangle  w×h .

Sample Input 

5 1 4
5 2 4
5 3 4
5 5 5
8 3 3

Sample Output 

0
5
11
12
3
 
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
#define maxn 10
// 代表一个网格节点
typedef struct cell
{
	int x, y;	//网格节点的坐标 

	// 构造函数
	cell(int x, int y)
	{
		this->x = x;
		this->y = y;
	}

	bool operator < (const struct cell& a) const
	{
		return x < a.x || (x == a.x && y < a.y);
	}
}cell;

// 一个Polyomino就是一堆cell的集合
typedef set<cell> poly;

// poly_set[i]代表有i个cell的poly集合
set<poly> poly_set[maxn+1];

// answer[n][w][h]的答案 
int answer[maxn+1][maxn+1][maxn+1];

void gen_poly();
void check_poly(const poly& this_p, cell& this_c);
poly normalize(poly& p);
poly rotate(poly& p);
poly flip(poly& p);


int main()
{
	// 生成所有poly
	gen_poly();
	
//	printf("here\n");
	int n, w, h;
	while(scanf("%d %d %d", &n, &w, &h) == 3)
	{
		printf("%d\n", answer[n][w][h]);	
	}	
	return 0;
}

int dic_x[4] = {-1,0,1,0};
int dic_y[4] = {0,1,0,-1};

// 生成所有poly
void gen_poly()
{
	for(int i = 1; i <= maxn; i++)
		poly_set[i] = set<poly>();

	// 先生成有1个cell的poly
	poly p1;
	p1.insert(cell(0,0));
	poly_set[1].insert(p1);

	// 分别根据有i-1个cell的poly集合来生成有i个cell的poly集合
	for(int i = 2; i <= maxn; i++)
	{
		// 对每个poly中的每个cell尝试在不同的四个方向增加一个cell
		for(set<poly>::iterator p = poly_set[i-1].begin(); p != poly_set[i-1].end(); p++)
		{
			for(poly::const_iterator q = p->begin(); q != p->end(); q++)
			{
				for(int j = 0; j < 4; j++)
				{
					cell new_c(q->x+dic_x[j], q->y+dic_y[j]);
//					cell new_c;
					if(p->find(new_c) == p->end())
					{
						// 检查形成的这个poly是否存在,如果不存在就加入
						check_poly(*p, new_c);
					}			
			
				}
			}	
		}	
	}

	// 对所有n,w,h生成答案
	for(int i = 1; i <= maxn; i++)
	{
		for(int w = 1; w <= i; w++)
		{
			for(int h = 1; h <= i; h++)
			{
				int count = 0;
				for(set<poly>::iterator p = poly_set[i].begin(); p != poly_set[i].end(); p++)
                		{
					int max_x = p->begin()->x, max_y = p->begin()->y;
					for(poly::iterator q = p->begin(); q != p->end(); q++)
					{
						if(max_x < q->x)
							max_x = q->x;
						if(max_y < q->y)
							max_y = q->y;
					}
					
					if(min(max_x, max_y) < min(w, h) && max(max_x, max_y) < max(w, h))
					{
						count++;	
					}	
				}
/*				if(count != 0)
					printf("answer[%d][%d][%d] = %d\n", i, w, h, count);
*/				answer[i][w][h] = count;	
			}
		}
	}								
}


// 检查形成的这个poly加上这个cell是否存在,如果不存在就加入
void check_poly(const poly& this_p, cell& this_c)
{
	poly p = this_p;
	p.insert(this_c);
	// 规范化到最小点为(0,0)
	p = normalize(p);

	int n = p.size();
	// 检查旋转的8个方向是否存在,如果不存在就加入到poly集合
	for(int i = 0; i < 4; i++)
	{
		if(poly_set[n].find(p) != poly_set[n].end())
			return;
		// 对该poly向右旋转90度
		p = rotate(p);		
	}
	// 将该poly向下反转180度
	p = flip(p);
	for(int i = 0; i < 4; i++)
        {
                if(poly_set[n].find(p) != poly_set[n].end())
                        return;
                // 对该poly向右旋转90度
                p = rotate(p);
        }
	poly_set[n].insert(p);
				
}

// 规范化到最小点为(0,0)
poly normalize(poly& p)
{
	poly this_p;
	int min_x = p.begin()->x, min_y = p.begin()->y;
	for(poly::iterator q = p.begin(); q != p.end(); q++)
	{
		if(q->x < min_x)
			min_x = q->x;
		if(q->y < min_y)
			min_y = q->y;	
	}
	for(poly::iterator q = p.begin(); q != p.end(); q++)    
        {
		this_p.insert(cell(q->x-min_x,q->y-min_y));	
        }		
	return this_p;	
}

// 对该poly向右旋转90度
poly rotate(poly& p)
{
	poly this_p;
	for(poly::iterator q = p.begin(); q != p.end(); q++)
        {
                this_p.insert(cell(q->y,-q->x));
        }
        return normalize(this_p);	
}


// 将该poly向下反转180度
poly flip(poly& p)
{
	poly this_p;
        for(poly::iterator q = p.begin(); q != p.end(); q++)
        {
                this_p.insert(cell(q->x,-q->y));
        }
        return normalize(this_p);
}

枚举所有的n连块,难点在于如何枚举,没有想出来,后来参考https://github.com/aoapc-book/aoapc-bac2nd/blob/master/ch7/UVa1602.cpp,自己实现了一遍。
难点:
1.以每个格子来扩展。先枚举1连块,在对1连块的每个格子的4个方向进行扩展,枚举2连块,依次类推。
2.将n连块表示成n个格子的集合,将所有的n连块又表示成集合,判重任务交给set.
3.判重时要将n连块进行8个方向的旋转,并且每个n连块需要规范化(左下角的格子在(0,0)).
4.得到n连块后判断是否能放进w*h的网格中,由于n连块已经规范化,得到n连块的格子最大x,y坐标,即能盛下该n连块的长和宽。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值