1. 程式人生 > >UVA 1602 Lattice Animals 回溯搜尋

UVA 1602 Lattice Animals 回溯搜尋

原題連結

求w(10)*h(10)的網格內不同的n(10)連塊的個數(不同是指不能通過平移,旋轉,翻轉得到).
(這種連塊學名叫做Polyomino,有很多有趣的性質,俄羅斯方塊中所有的塊都是4連塊)

情況有限,可以先搜尋再打表。

搜尋需要解決以下問題:

  1. 如何表示一種連塊?連塊的本質是若干個塊位置的集合,所以可以使用set<pair<int,int>>來表示一種連塊,把這個型別記為Poly.
  2. 如何在搜尋過程中擴充套件?維護一個當前可新增的方塊列表,每次擴充套件時從中選擇一個,然後更新這個列表。
  3. 如何判重?使用一個set<Poly>來儲存已經發現的所有連塊及它們的平移,旋轉,翻轉。這樣當發現一個新連塊時,它不在set裡時就表明它是沒有重複的。
  4. 平移,旋轉,翻轉如何進行?有一個簡潔的方法:初始位置從(0,0)開始,且連塊的每次擴充套件都在第一象限內,這樣就保證了所有連塊都是緊貼座標軸的,免去了平移的麻煩。如果記當前連塊的最大行是rm,最大列是cm,那麼沿橫軸翻轉後的Poly每個小方塊座標會從(r,c)變成(rm-r,cm),順時針旋轉90°之後會變成(cm-c,r).

一個省略了細節的實現:

const int M = 20;
const int go[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
using Coll = set<pair<int,int>>;
struct Poly
{
Coll st; int r, c; //0到r,0到c,閉區間 bool operator<(const Poly &b) const{ return st<b.st; } Poly rotate() {} Poly flip() {} void insert(int _r,int _c) { r = max(r,_r); c = max(c,_c); st.emplace(_a,_b); } void print() {} //輸出影象,方便除錯 }; int table[M][M][M]; set<Poly> st; //不可遞迴情況:已經存在,或者長度為10
inline bool limit(const Poly &su) {} inline void update(const Poly &su) {} void dfs(const Poly &u, const Coll &un) { if(limit(u)) return; update(u); for(auto pii:un) { Poly v = u; Coll vn = un; int r = pii.first, c = pii.second; v.insert(r,c); vn.erase(pii); for(int k=0;k<4;++k) { int nr = r+go[k][0], nc = c+go[k][1]; if(nr>=0 && v.st.count({nr,nc})==0) vn.emplace(nr,nc); } dfs(v,vn); } } int main(void) { dfs(Poly(),Coll({{0,0}})); int n,w,h; while(scanf("%d%d%d",&n,&w,&h)!=EOF) { if(w<h) swap(w,h); printf("%d\n",table[n][w][h] ); } return 0; }

可以仔細地研究一下上邊的程式碼。但實際上,它是錯的:下方的的這種連塊,它永遠也無法回溯得到。

**
 ***

因為它在得到

**
 *

之前就已經得到過

**
*

了,判重之後不會再向外擴充套件。

正確的做法應當把第一個塊放在地圖的中央(比如10,10),然後向四周擴充套件。判重時先平移到緊靠座標軸再判重。以下是完整程式碼。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 20;
const int go[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
using Coll = set<pair<int,int>>;
struct Poly
{
	Coll st; 
	int l, r, u, d; 
	Poly(int _l = 10,int _r = 0, int _u = 10, int _d = 0)
		: l(_l),r(_r),u(_u),d(_d){}
	bool operator<(const Poly &b) const{
		return st<b.st;
	}
	Poly stand() const
	{
		Poly res;
		for(auto pii:st)
			res.insert(pii.first-u,pii.second-l);
		return res;
	}
	Poly rotate() const
	{
		Poly res;
		for(auto pii:st)
			res.insert(r-pii.second,pii.first);
		return res;
	}
	Poly flip() const
	{
		Poly res;
		for(auto pii:st)
			res.insert(d-pii.first,pii.second);
		return res;
	}
	void insert(int _a,int _b)
	{
		st.emplace(_a,_b);
		l = min(l,_b);
		r = max(r,_b);
		u = min(u,_a);
		d = max(d,_a);
	}
	void print() const
	{
		system("cls");
		char mp[M][M];
		memset(mp,'.',sizeof(mp));
		for(auto pii:st)
			mp[pii.first][pii.second]='*';
		for(int i=0;i<M;++i)
		{
			for(int j=0;j<M;++j)
				putchar(mp[i][j]);
			printf("\n");
		}
		printf("\n");
	}
};

set<Poly> st;
int table[M][M][M];
//不可遞迴情況:已經存在,或者長度為10
inline bool limit(const Poly &su)
{
	int n = su.st.size();
	if(!n) return 0;

	Poly u = su.stand();
	if(st.count(u)) return 1;
	//u.print();
	for(int i=0;i<4;++i)	
	{
		st.insert(u);
		st.insert(u.flip());
		u = u.rotate();
	}

	int mi = min(u.d,u.r), ma = max(u.d,u.r);
	for(int i=ma+1;i<=n;++i) //大
		for(int j=mi+1;j<=i;++j) //小
			++table[n][i][j];

	return n==10;
}
void dfs(const Poly &u, const Coll &un)
{
	if(limit(u)) return;
	for(auto pii:un)
	{
		Poly v = u; Coll vn = un; 
		int r = pii.first, c = pii.second;
		v.insert(r,c); vn.erase(pii);
		for(int k=0;k<4;++k)
		{
			int nr = r+go[k][0], nc = c+go[k][1];
			if(nr>=0 && v.st.count({nr,nc})==0)
				vn.emplace(nr,nc);
		}
		dfs(v,vn);
	}
}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	dfs(Poly(),Coll({{10,10}}));
	int n,w,h;
	while(scanf("%d%d%d",&n,&w,&h)!=EOF)
	{
		if(w<h) swap(w,h);
		printf("%d\n",table[n][w][h] );
	}
    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}