[状压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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/11224014.html