hdu6341 /// 模拟 DFS+剪枝
题目大意:
将16行16列的矩阵分成四行四列共16块
矩阵的初始状态每行及每列都不会出现重复的元素
给定一个已旋转过某些块的矩阵 判断其是由初始状态最少经过几次旋转得到的
DFS枚举16个块的旋转方式
DFS过程中直接进行旋转 一旦发现旋转结果与之前枚举的块的旋转结果相悖就剪枝
这个剪枝已经足够AC 也不妨在加一条当前旋转次数比之前得到的可能答案大就剪枝
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define LL long long #define mem(i,j) memset(i,j,sizeof(i)) const int N=1e5+5; const int MOD=1e9+7; int G[20][20], g[20][20]; int R[20][20], C[20][20]; int ans; #define w(i) (i-1)*4 #define wG(i,j,I,J) G[w(i)+I][w(j)+J] int get(int r,int c,int x,int y,int k) { if(k==1) return wG(r,c,4-y+1,x); else if(k==2) return wG(r,c,4-x+1,4-y+1); else if(k==3) return wG(r,c,y,4-x+1); else return wG(r,c,x,y); } // 得到在r,c的块内x,y位置在第k种旋转之后的新数值 bool Rotate(int i,int j,int k) { bool OK=1; for(int I=1;I<=4;I++) for(int J=1;J<=4;J++) { int x=w(i)+I, y=w(j)+J; g[x][y]=get(i,j,I,J,k); int r=++R[x][g[x][y]]; int c=++C[y][g[x][y]]; if(r>1 || c>1) OK=0; // 这种旋转与之前其他块的旋转冲突 // 继续发展下去得到的一定是错误的 } return OK; } // 旋转i,j块 方式为第k种 void reRotate(int i,int j) { for(int I=1;I<=4;I++) for(int J=1;J<=4;J++) { int x=w(i)+I, y=w(j)+J; --R[x][g[x][y]]; --C[y][g[x][y]]; g[x][y]=G[x][y]; } } // 将i,j块的旋转取消 void dfs(int x,int y,int sum) { if(sum>ans) return; if(x==5) { ans=min(ans,sum); return; } // 四行四列16个块 到第五行说明已枚举了所有块的旋转 for(int i=0;i<=3;i++) { if(Rotate(x,y,i)==0) { reRotate(x,y); continue; } // 若发现这种旋转方式会冲突就跳过 if(y==4) dfs(x+1,1,sum+i); else dfs(x,y+1,sum+i); reRotate(x,y); } } // 搜索枚举16个块的旋转方式 int main() { int t; scanf("%d",&t); while(t--) { for(int i=1;i<=16;i++) { char s[20]; scanf("%s",s); for(int j=0;j<16;j++) { if(s[j]>='0' && s[j]<='9') G[i][j+1]=s[j]-'0'; else G[i][j+1]=s[j]-'A'+10; } } mem(R,0); mem(C,0); ans=INF; dfs(1,1,0); printf("%d\n",ans); } return 0; }