现在zoj暂时关了,实际上是在scuoj上做的。
看起来题目比较复杂,实际上主要需要思维的是如何恰当的剪枝及合适的DFS角度。
问题等价于将n*n个可能相同的方块放到一个n*n的表中,使满足题目要求的条件。由于放的时候是一个个放的,所以可以以此为切入点进行DFS,并且,只需要关注不同方块的种类,这样就可以极大的节约时间(从人做这件事的角度来看,相同的方块就像是相同的积木一样,人关注的只是相同的积木的形状以及个数。)
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 int a[25][4],kind[25],n,kge,b[25][4],cnt=0,m;//a记录不同种类方块各方向的数,kind记录每种种类的个数,kge记录种类数 5 int dfs(int p) 6 { 7 if(p==m)//dfs成功的话,就是进行到第n*n次(这时前0——n*n-1都已经放好) 8 return 1; 9 else 10 { 11 for(int i=0;i<kge;i++) 12 { 13 if(kind[i]==0) 14 continue; 15 else 16 { 17 if(p%n!=0)//p%n时左侧就没有方块 18 { 19 if(b[p-1][1]!=a[i][3]) 20 continue; 21 } 22 if(p>=n)//p<n时在最上一行,上面就没有方块 23 { 24 if(b[p-n][2]!=a[i][0]) 25 continue; 26 } 27 kind[i]--; 28 for(int j=0;j<4;j++) 29 { 30 b[p][j]=a[i][j]; 31 } 32 if(dfs(p+1))//继续dfs下一个位置 33 { 34 return 1; 35 } 36 kind[i]++;//如果这样并不行,恢复至之前的状态 37 } 38 } 39 return 0; 40 } 41 } 42 int main() 43 { 44 while(scanf("%d",&n)) 45 { 46 kge=0; 47 if(n==0) 48 break; 49 else 50 { 51 int up,left,right,down,i,j; 52 m=n*n; 53 for(i=0;i<m;i++) 54 { 55 scanf("%d%d%d%d",&up,&right,&down,&left); 56 for(j=0;j<kge;j++) 57 { 58 if(a[j][0]==up&&a[j][1]==right&&a[j][2]==down&&a[j][3]==left) 59 { 60 kind[j]++; 61 break; 62 } 63 } 64 if(j==kge)//整体是对有无与之前的方块相同的判断 65 { 66 a[kge][0]=up; 67 a[kge][1]=right; 68 a[kge][2]=down; 69 a[kge][3]=left; 70 kind[kge]=1; 71 kge++; 72 } 73 } 74 if(cnt) 75 puts("");//注意空行 76 if(dfs(0)) 77 printf("Game %d: Possible ",++cnt); 78 else 79 printf("Game %d: Impossible ",++cnt); 80 } 81 } 82 return 0; 83 }