题目描述
有一个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; }