题意:求能放进w*h的网格中的不同的n连通块个数(通过平移/旋转/翻转后相同的算同一种),1<=n<=10,1<=w,h<=n。
刘汝佳的题真是一道比一道让人自闭...QAQ~~
这道题没什么好的办法,Polya定理也毫无用武之地,只能暴力构造出所有可能的连通块,然后用set判重,比较考验基本功。
连通块可以用一个结构体D来表示,D的n代表黑块数量,然后有至多10个点P(x,y),用另一个结构体数组P[N]来表示。
问题的关键在于如何判重。
首先要知道set是通过<运算符来判重的,因此肯定要重载一下<运算符。既然要重载<运算符,那么就需要能比较出两个连通块的大小来。
如何比较两个黑块数量相同的连通块的大小呢?可以类比向量的字典序大小比较法,把所有的黑块按照x从小到大排序,x相同的按y从小到大排序,就可以比较大小了。
但是同构的两个连通块之间是不具有大小关系的,因此要先想办法把同构的连通块弄成统一的样子。
考虑三类等价变换:
1.平移:$(x,y)\leftrightarrow(x+a,y+b)$
2.旋转:$(x,y)\leftrightarrow(-y,x)$
3.翻转:$(x,y)\leftrightarrow(-x,y)$
所有同构的连通块都可以通过以上三类变换相互得到,对于同构的连通块,可以只保留其中字典序最小的。由于通过旋转和翻转能构造出的不同连通块只有8种,因此可以枚举这8中连通块,然后平移到左上角,取其中字典序最小的即可。
注意在比较字典序的时候,正反都要比较一下。(某人因为忘了反着比较而花了两个小时写了一整页代码来debug~~)
然后就没什么特别需要注意的了,设st[i][j][k]为用i个黑块能构造出j*k(j<=k)的异构连通块的集合,递推搞一搞就行了。
代码:(七重for循环,大概是我写过的层数最多的for循环了~~UVA的评测机也很给力,本地要跑300+ms,交上去30ms就过了)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=10+2,inf=0x3f3f3f3f; 5 const int dx[]= {0,0,-1,1}; 6 const int dy[]= {-1,1,0,0}; 7 //格点 8 struct P { 9 int x,y; 10 bool operator==(const P& b)const {return x==b.x&&y==b.y;} 11 bool operator<(const P& b)const {return x!=b.x?x<b.x:y<b.y;} 12 }; 13 //连通块 14 struct D { 15 int n; 16 P p[N]; 17 D(int _n):n(_n) {} 18 //字典序比较,重点 19 bool operator<(const D& b)const { 20 for(int i=0; i<n; ++i) { 21 if(p[i]<b.p[i])return 1; 22 if(b.p[i]<p[i])return 0; 23 } 24 return 0; 25 } 26 //旋转 27 D rot() { 28 D ret(n); 29 for(int i=0; i<n; ++i)ret.p[i]= {-p[i].y,p[i].x}; 30 return ret; 31 } 32 //翻转 33 D flip() { 34 D ret(n); 35 for(int i=0; i<n; ++i)ret.p[i]= {-p[i].x,p[i].y}; 36 return ret; 37 } 38 //平移到左上角 39 D norm() { 40 D ret=*this; 41 int dx=inf,dy=inf; 42 for(int i=0; i<n; ++i)dx=min(dx,ret.p[i].x),dy=min(dy,ret.p[i].y); 43 for(int i=0; i<n; ++i)ret.p[i].x-=dx,ret.p[i].y-=dy; 44 sort(ret.p,ret.p+n); 45 return ret; 46 } 47 //字典序最小的同构 48 D minimum() { 49 D a=this->norm(),b=a; 50 for(int i=0; i<2; ++i,b=b.flip()) 51 for(int j=0; j<4; ++j,b=b.rot()) { 52 b=b.norm(); 53 if(b<a)a=b; 54 } 55 return a; 56 } 57 }; 58 set<D> st[N][N][N]; 59 int n,w,h,ans; 60 61 int main() { 62 D t(1); 63 t.p[0]= {0,0}; 64 st[1][1][1].insert(t); 65 for(int _=1; _<10; ++_) { 66 for(int i=1; i<=10; ++i) 67 for(int j=i; j<=10; ++j) { 68 for(D t:st[_][i][j]) { 69 t.n++; 70 for(int k=0; k<t.n-1; ++k) { 71 for(int f=0; f<4; ++f) { 72 t.p[t.n-1]= {t.p[k].x+dx[f],t.p[k].y+dy[f]}; 73 bool ff=1; 74 for(int l=0; l<t.n-1; ++l)if(t.p[t.n-1]==t.p[l]) {ff=0; break;}//防止格点重复 75 if(!ff)continue; 76 int maxx=~inf,minx=inf,maxy=~inf,miny=inf; 77 for(int l=0; l<t.n; ++l) { 78 maxx=max(maxx,t.p[l].x); 79 minx=min(minx,t.p[l].x); 80 maxy=max(maxy,t.p[l].y); 81 miny=min(miny,t.p[l].y); 82 } 83 int tx=maxx-minx+1,ty=maxy-miny+1; 84 if(tx>ty)swap(tx,ty); 85 st[_+1][tx][ty].insert(t.minimum()); 86 } 87 } 88 } 89 } 90 } 91 while(scanf("%d%d%d",&n,&w,&h)==3) { 92 ans=0; 93 if(w>h)swap(w,h); 94 for(int i=1; i<=w; ++i) 95 for(int j=1; j<=h; ++j) 96 ans+=st[n][i][j].size(); 97 printf("%d\n",ans); 98 } 99 return 0; 100 }