[状压dp][dfs] Luogu P3160 局部极小值
题目描述
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
题解
- 其实就是个简单的状压dp,有两种不同的情况,当前的数可以填入坑内或不填入坑内,分类就好了
代码
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 char a[10]; 7 int m,n,mo=12345678,vis[6][10],f[29][(1<<8)+10],g[1<<9],mn[6][10],num,x[30],y[30],dx[9]={-1,-1,-1,0,0,1,1,1},dy[9]={-1,0,1,-1,1,-1,0,1}; 8 int dp() 9 { 10 memset(f,0,sizeof(f)),f[0][0]=1; 11 for (int i=0;i<(1<<num);i++) 12 { 13 g[i]=n*m,memset(vis,0,sizeof(vis)); 14 for (int j=0;j<num;j++) if (!(i&(1<<j))) for (int k=0;k<9;k++) vis[x[j]+dx[k]][y[j]+dy[k]]=1; 15 for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) if (vis[j][k]) g[i]--; 16 } 17 for (int i=1;i<=n*m;i++) 18 { 19 for (int j=0;j<(1<<num);j++) 20 { 21 if (g[j]-i+1>0) (f[i][j]+=f[i-1][j]*(g[j]-i+1))%=mo; 22 for (int k=0;k<num;k++) if((1<<k)&j) (f[i][j]+=f[i-1][j^(1<<k)])%=mo; 23 } 24 } 25 return f[n*m][(1<<num)-1]; 26 } 27 int dfs(int X,int Y) 28 { 29 if (Y==m+1) X++,Y=1; 30 if (X==n+1) return dp(); 31 int ans=dfs(X,Y+1); 32 for (int i=0;i<9;i++) if (mn[X+dx[i]][Y+dy[i]]) return ans; 33 x[num]=X,y[num++]=Y,mn[X][Y]=1,ans-=dfs(X,Y+1),ans=(ans+mo)%mo,mn[X][Y]=0,num--; return ans; 34 } 35 int main() 36 { 37 scanf("%d%d",&n,&m); 38 for (int i=1;i<=n;i++) 39 { 40 scanf("%s",a+1); 41 for (int j=1;j<=m;j++) if (a[j]=='X') mn[i][j]=1,x[num]=i,y[num++]=j; 42 } 43 for (int i=0;i<num;i++) for (int j=0;j<i;j++) if (abs(x[i]-x[j])<2&&abs(y[i]-y[j])<2) return printf("0"),0; 44 if (!num) printf("0"); else printf("%d",dfs(1,1)); 45 }