题解 P1391 【方阵安排】
看到这道题,我们可以先想到简单粗暴的算法:枚举每个
考虑如何进一步优化。我们发现,时间主要浪费在“枚举每个
我们考虑换一种思维方式。我们能否枚举出所有的完美的方阵,然后检查输入的初始方阵能否变换到这个方阵,并花费尽可能少的步数?
如果还是一个一个位置去枚举整个方阵,这样还是会超时。但是,如果我们能枚举出方阵的一部分,通过这一部分来确定整个方阵, 我们就可以极大地节省我们花费的时间。
我们考虑方阵中的一个格子
-
当格子
(x+1,y),(x-1,y),(x,y-1) 都确定了,可以推出格子(x,y+1) 的状况。那么初始情况要枚举(x,1),x \in [1,n] 这些格子的情况。即枚举第一行的状况进而确定整个方阵的状况。 -
当格子
(x,y+1),(x-1,y),(x,y-1) 都确定了,可以推出格子(x+1,y) 的状况。那么初始情况要枚举(1,y),y \in [1,n] 这些格子的情况。即枚举第一列的状况进而确定整个方阵的状况。
因为
通过确定第一行推出整个矩阵的代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define rgt register int
#define qmn(a,b) a<b?a:b
using namespace std;
int n,ans=1010; //18*18=324<1010,所以答案不可能达到 1010,如果枚举后 ans 值没有改变就是无解
int mtx[20][20],inx[20][20];
int fx[4]={1,-1,0,0};
int fy[4]={0,0,-1,1};
void dfs(int xi,int g){
if(xi>n){
int tans=g; //记录第一行变换的位置数
for(rgt i=1;i<n;i++){
for(rgt c,j=1;j<=n;j++){
c=0;
for(rgt tx,ty,k=1;k<4;k++){
tx=i+fx[k];
ty=j+fy[k];
if(tx>=1&&ty>=1&&tx<=n&&ty<=n){
if(mtx[tx][ty])
c++;
}
} //统计周围三个位置上 1 的数量
if(c%2){ //确定下一行的对应位置上是 1
mtx[i+1][j]=1;
if(!inx[i+1][j]) //与初始矩阵不同
tans++; //变换位置数+1
}
else{ //确定下一行对应位置上是 0
mtx[i+1][j]=0;
if(inx[i+1][j]) //因为 1 不能变换为 0,此方案不可行
return;
}
}
}
ans=qmn(ans,tans); //取最小变换次数作为答案
return;
}
mtx[1][xi]=inx[1][xi]; //得到初始矩阵的第一行
dfs(xi+1,g); //不变换
if(!mtx[1][xi]){ //变换
mtx[1][xi]=1;
dfs(xi+1,g+1);
mtx[1][xi]=0;
}
}
int main(){
scanf("%d",&n);
for(rgt i=1;i<=n;i++)
for(rgt j=1;j<=n;j++)
scanf("%d",&inx[i][j]);
dfs(1,0);
if(ans==1010)
printf("-1");
else
printf("%d",ans);
return 0;
}