题目描述
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
输入格式
输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中”X“表示局部极小值,”.“表示非局部极小值。
输出格式
输出仅一行,为可能的矩阵总数除以12345678的余数。
输入输出样例
输入 #1复制
3 2
X.
..
.X
输出 #1复制
60
SOLUTION:
见洛谷
CODE:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char a[10];
int m,n,min[6][10];//描述整个矩阵
int num,x[30],y[30];//描述坑的个数、位置
int w=12345678;//膜
int dx[10]={-1,-1,-1,0,0,1,1,1,0};//移动位置
int dy[10]={-1,0,1,-1,1,-1,0,1,0};
int vis[6][10],f[29][(1<<8)+10],hi[1<<9];//dp用到的东西
int dp(){
int i,j,k;
memset(f,0,sizeof(f));
f[0][0]=1;
for(i=0;i<(1<<num);i++){//预处理出每个状态i对应的可填点数量
hi[i]=n*m;
memset(vis,0,sizeof(vis));
for(j=0;j<num;j++)if(!(i&(1<<j)))for(k=0;k<9;k++)vis[x[j]+dx[k]][y[j]+dy[k]]=1;
for(j=1;j<=n;j++)for(k=1;k<=m;k++)if(vis[j][k])hi[i]--;
}
for(i=1;i<=n*m;i++){//枚举填哪个数
for(j=0;j<(1<<num);j++){//枚举状态
if(hi[j]-i+1>0)f[i][j]+=f[i-1][j]*(hi[j]-i+1),f[i][j]%=w;
for(k=0;k<num;k++)if((1<<k)&j)f[i][j]+=f[i-1][j^(1<<k)],f[i][j]%=w;
}
}
return f[n*m][(1<<num)-1];
}
int cnt=0;
int P=0;
int dfs(int X,int Y){
if(Y==m+1)X++,Y=1;
if(X==n+1)
{
if(cnt%2==1)P-=dp();else P+=dp();
P%=w;P+=w;P%=w;
return 0;
}
dfs(X,Y+1);
int i;
for(i=0;i<9;i++)if(min[X+dx[i]][Y+dy[i]])return 1;
//如果没return说明这个地方是个可能变成坑的地方,那就把它变成坑dfs一下
x[num]=X;y[num++]=Y;
min[X][Y]=1;
cnt++;
dfs(X,Y+1);
min[X][Y]=0;num--;//别忘了变回去
cnt--;
return 1;
}
int main(){
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%s",a+1);
for(j=1;j<=m;j++)if(a[j]=='X'){
min[i][j]=1;
x[num]=i;y[num++]=j;
}
}
for(i=0;i<num;i++)for(j=0;j<i;j++)if(abs(x[i]-x[j])<2&&abs(y[i]-y[j])<2)return printf("0"),0;
if(!num)return printf("0"),0;
dfs(1,1);
printf("%d",P);
return 0;
}