版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83871234
求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;
}