UVA 1602 Lattice Animals 回溯搜尋
阿新 • • 發佈:2018-12-12
求w(10)*h(10)的網格內不同的n(10)連塊的個數(不同是指不能通過平移,旋轉,翻轉得到).
(這種連塊學名叫做Polyomino,有很多有趣的性質,俄羅斯方塊中所有的塊都是4連塊)
情況有限,可以先搜尋再打表。
搜尋需要解決以下問題:
- 如何表示一種連塊?連塊的本質是若干個塊位置的集合,所以可以使用set<pair<int,int>>來表示一種連塊,把這個型別記為Poly.
- 如何在搜尋過程中擴充套件?維護一個當前可新增的方塊列表,每次擴充套件時從中選擇一個,然後更新這個列表。
- 如何判重?使用一個set<Poly>來儲存已經發現的所有連塊及它們的平移,旋轉,翻轉。這樣當發現一個新連塊時,它不在set裡時就表明它是沒有重複的。
- 平移,旋轉,翻轉如何進行?有一個簡潔的方法:初始位置從(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;
}