1. 程式人生 > >UVa 1602 Lattice Animals

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 
freen-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 nw, and h (1$ \le$n$ \le$101$ \le$wh$ \le$n). 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.
5 1 4
5 2 4
5 3 4
5 5 5
8 3 3
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連塊的長和寬。