[Uva1602][Poj2170][Zoj2669][Northeastern Europe 2004] Lattice Animals 【set+傻瓜搜索】

题目大意:

给定n,w,h,(n ≤ 10, 1 ≤ w, h ≤ n),求n个小正方形组成的图案能嵌套在w * h 的矩形中的方案。

如图第一行数据输入为(5,2,4),第二行数据输入为(8,3,3)

样例输入:

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

样例输出:

0
5
11
12
3



卤煮番(xian)外(che)(大晚上太寂寞了闲扯一番):

①平生第一次写set,下笔(jianpan)的时候堪比蒟蒻考高中物理(物理弱)。。。

③STQ  STL  OTZ 神器orz%

②参考资料:http://blog.csdn.net/c21c21/article/details/45044105   %%%大神

④你发现编号错了吗


正经

误导思路:

①开始最水的就是开bool数组模拟选还是不选,然后再判定是否合法,先是时间超了,然后发现判重根本不好判,算了舍了。

②然后我就想不出来了……


参考思路:

之所以觉得是参考思路是觉得肯定应该大概估计还有其他做法吧,作为一只猹我暂时还不知道。。。

思路大概就是先预处理最后O(1)出结果。

预处理的过程:设当前预处理到cur个小块了

①在set[cur - 1]枚举cur - 1个小块组成的大连块p。

再枚举大连块p中的每个小块q。

在q的四周(有空白的地方)填上一个小块,组成新的大连块nxt (cur小块组成)。

    注:每次对大连块进行后有可能会使画面移位,所以处理一次,把它移到一个统一的水准上,即下边界和左边界与x轴y轴重合(normal())

④旋转+翻转在set[cur]里判重,重了就不加进set[cur],没重就加进。

    注:同上

⑤处理完所有的set[  ]后枚举i(小块个数),w(宽),h(高)答案用ans[i][w][h]保存。



贴贴贴:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
const int inf = 1 << 30;
const int maxn = 10;
using namespace std;
int n, w, h;
int ans[maxn + 5][maxn + 5][maxn + 5];
int add[4][2] = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}};

struct cell//小块
{
	int x, y;
	cell(){}
	cell(int a, int b){x = a, y = b;}
	bool operator < (const cell& a)const{
		return x < a.x || (x == a.x && y < a.y);
	}
};

typedef set<cell> pat;//大连块(小块的集合)
set<pat> patset[maxn + 5];//大连块的集合

void print(pat cur)//调试用
{
	for(pat::iterator p = cur.begin(); p != cur.end(); p ++)
		printf("%d %d\n", p -> x, p -> y);
	puts("");
}

pat normal(const pat& cur)//使大连块的左边界与y轴对齐,下边界与x轴对齐
{
	pat newpat;
	int ldx = inf, ldy = inf;//当前的左边界与下边界
	for(pat::iterator p = cur.begin(); p != cur.end(); p ++)
		ldx = min(ldx, p -> x), ldy = min(ldy, p -> y);
	for(pat::iterator p = cur.begin(); p != cur.end(); p ++)
		newpat.insert(cell(p -> x - ldx, p -> y - ldy));
	return newpat;
}

pat rotate(const pat& cur)//顺时针旋转90°
{
	pat newpat;
	for(pat::iterator p = cur.begin(); p != cur.end(); p ++)
		newpat.insert(cell(p -> y, -(p -> x)));
	return normal(newpat);
}

pat flip(const pat& cur)//左右翻转
{
	pat newpat;
	for(pat::iterator p = cur.begin(); p != cur.end(); p ++)
		newpat.insert(cell(-(p -> x), p -> y));
	return normal(newpat);
}

void check(const pat& cur, const cell &addcell)//8次判重
{
	pat newcur = cur;
	
	newcur.insert(addcell);
	newcur = normal(newcur);
	
	int Size = newcur.size();
	
	for(int i = 1; i <= 4; i ++){//该处有4次	
		if(patset[Size].find(newcur) != patset[Size].end())
			return;
		newcur = rotate(newcur);
	}
	
	newcur = flip(newcur);
	for(int i = 1; i <= 4; i ++){//该处又有4次		
		if(patset[Size].find(newcur) != patset[Size].end())
			return;
		newcur = rotate(newcur);
	}
	
	patset[Size].insert(newcur);//终于完辣,进队
}

void pre()
{

	cell nxt;
	pat t;
	int maxx,maxy;

	t.insert(cell(0, 0));//一个小块的情况(1)
	patset[1].insert(t);
	
	for(int i = 2; i <= maxn; i ++)//枚举前一个状态
		for(set<pat>::iterator p = patset[i - 1].begin(); p != patset[i - 1].end(); p ++)
			for(pat::iterator q = p->begin(); q != p->end(); q ++)
				for(int j = 0; j < 4; j ++){
					nxt = cell(q -> x + add[j][0], q -> y + add[j][1]);//要填的小块

					if(p -> find(nxt) == p -> end()){//如果小块在前一个(i - 1)大连块状态中没被覆盖(当前是空白)
						check(*p, nxt);//判判判
					}
				}
	/*for(int i = 1; i <= 6 ; i ++){//调试
		printf("%d:\n",i);
		for(set<pat>::iterator p = patset[i].begin(); p != patset[i].end(); p ++)
			print(*p);
	}*/

	for(int i = 1; i <= maxn; i ++)//保存答案
		for(int j = 1; j <= i; j ++)
			for(int k = 1; k <= i; k ++)
				for(set<pat>::iterator p = patset[i].begin(); p != patset[i].end(); p ++){
					maxx = 0, maxy = 0;//当前大连块的边界
					for(pat::iterator q = p -> begin(); q != p -> end(); q ++)
						maxx = max(maxx, q -> x), maxy = max(maxy, q -> y);
					if((maxx < j && maxy < k) || (maxx < k && maxy < j))//如果当前大连块可以被j * k的矩形嵌套
						ans[i][j][k] ++;
				}
}	

int main()
{
	pre();
	while(scanf("%d%d%d",&n ,&w, &h) != EOF){
		printf("%d\n", ans[n][w][h]);//苗条的主函数
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值