传送门

题意:

有一个n行m列的整数矩阵,其中1到 nm n ∗ m 之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。给出所有局部极小值的位置,判断有多少个可能的矩阵。

(1n4,1m7) ( 1 ≤ n ≤ 4 , 1 ≤ m ≤ 7 )

Solution:

显然我们把矩阵所有的状态存下来是不行的,所以说我们考虑减少我们需要存储的状态:我们的局部极小值最多只会有8个

那么我们也可以对这8个极小值进行状压

考虑将值从小到大填: f[i][S] f [ i ] [ S ] 表示有S状态的局部极小值已经被填了,已经填了1~i的方案数

这样我们每次转移时考虑这个值填在局部极小值的位置还是其他位置:

cnt[S] c n t [ S ] 表示当局部极小值填的状态是S时,不填到局部极小值有多少种填法,因为还没填的局部极小值的周围都不能先填,所以这个可以简单地计算出来

转移即为 f[i][S]=f[i1][S](cnt[S]i+1)+pS,|p|=1f[i1][Sp] f [ i ] [ S ] = f [ i − 1 ] [ S ] ∗ ( c n t [ S ] − i + 1 ) + ∑ p ⊆ S , | p | = 1 f [ i − 1 ] [ S − p ]

但是这样求可能会求出有更多局部极小值的方案,所以我们需要容斥

我们用总方案数-多出一个极小值的方案数+多出两个极小值的方案数…

总复杂度上限 O(2828nm) O ( 2 8 ∗ 2 8 ∗ n ∗ m )

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mod=12345678;
int f[30][1<<8];
int n,m,qx[10],qy[10],cnt[1<<8],ans;
char s[10];
bool tag[10][10],vis[10][10];
int dx[8]={
  0,0,1,-1,1,1,-1,-1};
int dy[8]={
  1,-1,1,-1,0,-1,0,1};
void dp(int v)
{
    int tot=0;
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (tag[i][j]) qx[tot]=i,qy[tot]=j,tot++;
    for (int S=0;S<(1<<tot);S++)
    {
        cnt[S]=0;for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) vis[i][j]=0;
        for (int i=0;i<tot;i++)
            if (!((S>>i)&1))
            {
                for (int k=0;k<8;k++)
                {
                    int nx=qx[i]+dx[k],ny=qy[i]+dy[k];
                    if (nx<1||nx>n||ny<1||ny>m)  continue;
                    vis[nx][ny]=1;
                }
                vis[qx[i]][qy[i]]=1;
            }
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++) if (!vis[i][j]) cnt[S]++; 
    //  cout<<S<<" "<<cnt[S]<<endl;
    }
    f[0][0]=1;
    for (int i=1;i<=n*m;i++)
        for (int S=0;S<(1<<tot);S++)
        {
            f[i][S]=1ll*f[i-1][S]*(cnt[S]-i+1)%mod;
            for (int j=0;j<tot;j++)
                if ((S>>j)&1) f[i][S]=(f[i-1][S-(1<<j)]+f[i][S])%mod;
        }
    ans=(ans+v*f[n*m][(1<<tot)-1]+mod)%mod;
}
void dfs(int x,int y,int ifi)
{
    if (y==m+1) {dfs(x+1,1,ifi);return;}
    if (x==n+1) {dp(ifi);return;}
    dfs(x,y+1,ifi);
    bool can=1;
    for (int k=0;k<8;k++)
    {
        int nx=x+dx[k],ny=y+dy[k];
        if (nx<1||nx>n||ny<1||ny>m)  continue;
        if (tag[nx][ny]) can=0;
    }
    if (tag[x][y]) can=0;
    if (can) tag[x][y]=1,dfs(x,y+1,-ifi),tag[x][y]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s+1);for (int j=1;j<=m;j++) if (s[j]=='X') tag[i][j]=1;
    }
    dfs(1,1,1);
    printf("%d",ans);
}