题面
题意
将1,2,3…,m*n填入一个 m ∗ n m*n m∗n的矩阵,要求一些位置满足比周围八个数都小,且其他位置不满足,则一共有几种方案.
做法
首先不考虑"其他位置不满足"这个要求,因为数据范围很小,因此最多有8个位置符合条件,可以状压dp.考虑将
m
∗
n
m*n
m∗n个数从小到大依次填入矩阵,用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示当前要求位置填的数的状态为i,其他位置一共填了j个的方案数,即可求出答案.
然后考虑其他位置,可以进行容斥,暴力搜索出哪些位置也满足比周围的数都小,然后用上述dp即可求出方案数,从而得到答案.
代码
#include<bits/stdc++.h>
#define ll long long
#define N 10
#define M 12345678
using namespace std;
ll m,n,ans,x[N],y[N],dp[260][30];
char mm[N][N];
inline void Add(ll &u,ll v){u=(u+v)%M;}
inline void GG()
{
puts("0");
exit(0);
}
inline ll calc()
{
ll i,j,k,ii,jj,cx=0;
bool ok[N][N];
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(mm[i][j]=='X')
{
cx++;
x[cx]=i;
y[cx]=j;
}
}
}
for(i=0;i<(1 << cx);i++) memset(dp[i],0,sizeof(dp[i]));
dp[0][0]=1;
for(i=0;i<(1 << cx);i++)
{
memset(ok,1,sizeof(ok));
ll cnt=0;
for(j=1;j<=cx;j++)
{
if((1 << (j-1))&i) continue;
for(ii=x[j]-1;ii<=x[j]+1;ii++)
{
for(jj=y[j]-1;jj<=y[j]+1;jj++)
{
ok[ii][jj]=0;
}
}
}
for(ii=1;ii<=m;ii++)
{
for(jj=1;jj<=n;jj++)
{
if(mm[ii][jj]=='X') continue;
cnt+=ok[ii][jj];
}
}
for(j=0;j<=m*n-cx;j++)
{
if(!dp[i][j]) continue;
for(k=1;k<=cx;k++)
{
if((1 << (k-1))&i) continue;
Add(dp[i|(1 << (k-1))][j],dp[i][j]);
}
if(j<cnt) Add(dp[i][j+1],dp[i][j]*(cnt-j)%M);
}
}
return dp[(1 << cx)-1][m*n-cx];
}
void dfs(ll u,ll v,ll zf)
{
if(u>m)
{
Add(ans,M+zf*calc());
return;
}
ll i,j;
if(v==n) dfs(u+1,1,zf);
else dfs(u,v+1,zf);
for(i=u-1;i<=u+1;i++)
{
for(j=v-1;j<=v+1;j++)
{
if(mm[i][j]=='X') break;
}
if(j<=v+1) break;
}
if(i>u+1)
{
mm[u][v]='X';
if(v==n) dfs(u+1,1,-zf);
else dfs(u,v+1,-zf);
mm[u][v]='.';
}
}
int main()
{
ll i,j;
cin>>m>>n;
for(i=1;i<=m;i++) scanf("%s",mm[i]+1);
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(mm[i][j]=='X' && mm[i-1][j]=='X') GG();
if(mm[i][j]=='X' && mm[i][j-1]=='X') GG();
}
}
dfs(1,1,1);
cout<<ans;
}