题目大意:
给定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]);//苗条的主函数
}
}