网格动物UVA1602


题目大意

输入n,w,h(1<=n<=10,1<=w,h<=n).求能放在w*h网格里的不同的n连块的个数(平移,旋转,翻转算一种)

  首先,方法上有两个,一是打表,dfs构造连通块,枚举出来后再进行判重,另一种就是直接枚举每种连通块,保证每种连通块只枚举一次(这个方法还不会。。但可以访问en.wikipedia.org/wiki/Polyomino 进行学习)
  先看第一种方法:
    1.如何构造连通块?
    因为是构造连通块,所以就不能像寻找连通块或寻找路径那么做。。。一开始就犯了这种错误。。应该从大小为X(初始时X为1)的连通块开始,尝试从它的每一个边向外扩展,得到一个大小为X+1的连通块,并把这个连通块来判重,如果没有重,那么就将构成它的每一小块的坐标记录下来,为接下来用这个连通块构造更大的连通块做准备。如此循环往复,直到你求得大小为n的所有连通块。
    2.如何进行判重?
    判重也是这个题中很麻烦的一个点。。大体上的思路就是用存下每个连通块的状态,包括它们进行一系列变化后的状态。在存储这些千奇百怪的连通块时,务必保证把每个连通块顶着二维平面的左上角存,即以(0,0)为中心,将它们坐标标准化。(意会一下。。)如果你用的是set来存储以前构造到的连通块,那么你在判重的时候可以简单地用count函数,如果是其他数据类型,就只能挨个枚举比较了

点击查看代码
#include<bits/stdc++.h>
using namespace std; 
const int maxn = 10;
struct cell {//块
	int x, y;
	cell(int x1, int y1):x(x1),y(y1) {}
	bool operator<(const cell& a)const {//因为set需要定义<
		return x < a.x || ((x == a.x) && y < a.y);
	}
};

typedef set<cell> poly;//n连块
set<poly> poly_set[maxn+1];//poly_set[i]是含i个块的(规范化的poly)的集合
int ans[maxn+1][maxn+1][maxn+1];//存答案
int dx[4] = { -1,0,1,0 };
int dy[4] = { 0,1,0,-1 };

poly normalize(poly p) {//规范化,贴着左上角。可能并不占据(0,0)这个点。
	poly newp;
	int minx = p.begin()->x, miny = p.begin()->y;
	for (auto q = p.begin(); q != p.end(); q++) {
		minx = min(minx, q->x);
		miny = min(miny, q->y);
	}
	for (auto q = p.begin(); q != p.end(); q++)
		newp.insert(cell(q->x - minx, q->y - miny));
	return newp;
}
poly rotate(poly p) {//顺指针旋转90°
	poly newp;
	for (auto q = p.begin(); q != p.end(); q++)
		newp.insert(cell(q->y, -q->x));
	return normalize(newp);
}
poly flip(poly p) {//上下翻转
	poly newp;
	for (auto q = p.begin(); q != p.end(); q++)
		newp.insert(cell(q->x, -q->y));
	return normalize(newp);
}

void check_poly(poly p, cell& c) {//c能否放进p中
	p.insert(c);
	p = normalize(p);//规范化
	int n = p.size();
	//旋转八个方向,如果不存在的话加入到poly_set
	for (int i = 0; i < 4; i++) {
		if (poly_set[n].count(p))
			return;
		p = rotate(p);//顺时针旋转90°
	}
	p = flip(p);//翻转
	for (int i = 0; i < 4; i++) {
		if (poly_set[n].count(p))
			return;
		p = rotate(p);//顺时针旋转90°
	}
	poly_set[n].insert(p);
}

void gen_poly() {//生成所有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);
	//int c = 0; //用来看运算量 60000 在我的电脑上用了十几秒但交上去能过,挺玄学的
	//根据含i-1个cell的poly_set推出含i个的poly_set
	for (int i = 2; i <= maxn; i++)
		for (auto p = poly_set[i - 1].begin(); p != poly_set[i - 1].end(); p++)
			//p类型是set<poly>::iterator
			for (auto q = p->begin(); q != p->end(); q++) {
				//q类型是set<cell>::iterator ,也就是poly::iterator
				for (int j = 0; j < 4; j++) {
					/*if (c++ % 10000==0) {
						cout << c / 10000;
					}*/

					cell newc(q->x + dx[j], q->y + dy[j]);
					if (!p->count(newc))//p中没有newc这个cell,就加入
						check_poly(*p, newc);//别忘p是迭代器
				}
			}
	for (int i = 1; i <= maxn; i++)//i连块
		for (int w = 1; w <= i; w++)
			for (int h = 1; h <= i; h++) {
				int count = 0;
				for (auto p = poly_set[i].begin(); p != poly_set[i].end(); p++) {
					int maxx = p->begin()->x, maxy = p->begin()->y;
					for (auto q = p->begin(); q != p->end(); q++) {
						maxx = max(maxx, q->x);
						maxy = max(maxy, q->y);
					}
					if(min(maxx,maxy)<min(w,h)&&max(maxx,maxy)<max(h,w))
						//因为有两种摆法:横放或竖放。是(maxx<w&&maxy<h)||(maxx<h&&maxy<w)的简写
						//为什么没有等于?因为规范化的时候左上角是(0,0)
						count++;
				}
				ans[i][w][h] = count;
			}
}

int main() {
	gen_poly();//生成所有poly并存答案
	cout << 1;
	int n, w, h;
	while (scanf_s("%d %d %d", &n, &w, &h) == 3)
		cout << ans[n][w][h] << endl;
	return 0;
}

原创文章,作者:bd101bd101,如若转载,请注明出处:https://blog.ytso.com/271706.html

(0)
上一篇 2022年7月6日
下一篇 2022年7月6日

相关推荐

发表回复

登录后才能评论