UVa 1602 Lattice Animals 网格动物

本文介绍UVa1602 Lattice Animals的解题方法,通过预处理所有可能的答案实现高效求解。文章详细解释了如何存储连块、处理旋转和平移、以及判别重复等问题,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:UVa 1602 Lattice Animals 网格动物

题目翻译请见《入门经典》 P212

分析:

提问很多,每次搜一次你就炸了,只有先处理出所有的答案,也就是打表大法!

本题没有什么剪枝,直接暴力即可,主要是有以下三个问题:

(1)怎么存储一个连块

(2)怎么处理旋转,平移和翻转

(3)怎么判重


对于存储,采用一个坐标的集合来表示连块 ,坐标用pair 来存储,连块就用set< pair<int,int> >。

存储的所有连块,都先进行标准化,也就是把最左上的点放到(0,0)的位置,这样也就处理了平移的操作。

对于旋转,不妨考虑顺时针旋转90 度, 使用坐标变换 (x,y)->(y,-x)。

对于翻转,坐标变化为(x,y)->(x,-y)

注意,旋转或者翻转之后都要标准化!

讨论的先后也是本题的关键: 如果先定下w,h搜索不超过范围的连块就重复搜索了很多情况,所以不妨先扩展出所有的连块,再讨论w*h的格子装不装得下。

代码如下:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<set>

#define PII pair<int,int>
#define iter set<PII>:: iterator
#define xx first
#define yy second
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=10+5,inf=0x3f3f3f3f;
const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};

int n,w,h;
int ans[maxn][maxn][maxn];

typedef set<PII> poly;     //连块 

set<poly> vis[15];

void Print(poly p){    //调试用: 打印这个连块 
	int size=p.size();
	cout<<"A ploy of size "<<size<<": \n";
	for(iter it=p.begin(); it!=p.end(); it++)
		cout<<"cell : ("<<it->xx<<" , "<<it->yy<<" ) "<<endl;
}

inline poly Standardize(poly p){   //标准化:把最左上的点移到(0,0) 
	int Minx=inf,Miny=inf;
	poly ret;
	iter it;
	for(it=p.begin();it!=p.end();it++)
		Minx=min(Minx,it->xx),Miny=min(Miny,it->yy);
	for(it=p.begin();it!=p.end();it++)
		ret.insert(make_pair(it->xx-Minx,it->yy-Miny));
	return ret;
}

inline poly Rotate(poly p){   //顺时针旋转90度  (x,y) -> (y,-x) 
	poly ret;
	for(iter it=p.begin(); it!=p.end(); it++)
		ret.insert(make_pair(it->yy,-(it->xx) ));
	return Standardize(ret);
}

inline poly Flip(poly p){  //翻转  (x,y)->(x,-y) 
	poly ret;
	for(iter it=p.begin(); it!=p.end(); it++)
	 	ret.insert(make_pair(it->xx,-(it->yy)));
	return Standardize(ret);
}


bool Check(poly p){  //检查该连块(已标准化) 有没有出现过 ,如果没有,插入Vis集合中 
	int i,size=p.size();
	if(vis[size].count(p)) return false;
	for(i=0;i<4;i++){
		p=Rotate(p);
		if(vis[size].count(p)) return false;
	}
	p=Flip(p);
	if(vis[size].count(p)) return false;
	for(i=0;i<4;i++){
		p=Rotate(p);
		if(vis[size].count(p)) return false;
	}
	vis[size].insert(p);
	return true;
}

void DFS(poly p){    //由当前k连块扩展出k+1 连块 
	if(p.size()==n){
		Check(p);
		return ;
	}
	for(iter it=p.begin(); it!=p.end() ; it++){
		for(int i=0;i<4;i++){
			PII cur;
			cur.xx=it->xx+dx[i]; cur.yy=it->yy +dy[i];
			if(!p.count(cur)){
				poly temp=p;
				temp.insert(cur);
				DFS(temp);
			}
		}
	}
}

void Make_Table(){    //提问次数多,先计算好所有答案 
	poly S;
	S.insert(make_pair(0,0)); //1连块
	vis[1].insert(S);
	for(n=2;n<=10;n++){
		set<poly> :: iterator it;
		for(it=vis[n-1].begin();it!=vis[n-1].end(); it++)
			DFS(*it);
	}
	for(n=1;n<=10;n++){
		set<poly>::iterator it;
		for(it=vis[n].begin(); it!=vis[n].end(); it++){
			int Maxx=0,Maxy=0;
			for(iter i=it->begin();i!=it->end(); i++){
				Maxx=max(Maxx,i->xx);
				Maxy=max(Maxy,i->yy);
			}
			if(Maxx<Maxy) swap(Maxx,Maxy);
			for(w=1;w<=10;w++)
				for(h=1;h<=10;h++)
					if(Maxx<max(w,h) && Maxy<min(w,h)) ans[n][w][h]++;
		}
	}
}

int main(){
	Make_Table();
	while(scanf("%d%d%d",&n,&w,&h)==3)
		printf("%d\n",ans[n][w][h]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值