Description
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
Input
输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。
Output
输出仅一行,为可能的矩阵总数除以12345678的余数。
Sample Input
3 2
X.
..
.X
X.
..
.X
Sample Output
60
HINT
Source
状压DP+容斥
考虑局部极小值不超过8个 所以状压
把1-n*m一个一个填进去
预处理状态为j的能填的格子数f[j]
DP即可
但是这样只满足给定格子是局部极小值,不保证其他不是局部极小值
暴力dfs回溯容斥即可
复杂度O(能过)
#include <bits/stdc++.h>
using namespace std;
const int mod = 12345678;
int dx[9] = { 1, 1, 0, -1, -1, -1, 0, 1, 0 };
int dy[9] = { 0, -1, -1, -1, 0, 1, 1, 1, 0 };
char mp[10][10];
int n, m, ans, cnt, dp[29][256], f[256];
pair < int, int > st[10];
bool vis[10][10];
inline void upd(int &x, int y) { x += y; if( x >= mod ) x -= mod; if( x < 0 ) x += mod; }
inline int getdp()
{
memset( f, 0, sizeof( f ) );
memset( dp, 0, sizeof( dp ) );
dp[ 0 ][ 0 ] = 1;
cnt = 0;
for( int i = 1 ; i <= n ; i++ )
for( int j = 1 ; j <= m ; j++ )
if( mp[ i ][ j ] == 'X' )
st[ ++cnt ] = make_pair( i, j );
for( int i = 0 ; i < ( 1 << cnt ) ; i++ )
{
memset( vis, 0, sizeof( vis ) );
for( int j = 1 ; j <= cnt ; j++ ) if( !( ( i >> j - 1 ) & 1 ) )
for( int k = 0 ; k < 9 ; k++ )
{
int nx = st[ j ].first + dx[ k ], ny = st[ j ].second + dy[ k ];
if( !nx || !ny || nx > n || ny > m ) continue;
vis[ nx ][ ny ] = 1;
}
for( int j = 1 ; j <= n ; j++ )
for( int k = 1 ; k <= m ; k++ )
if( !vis[ j ][ k ] )
f[ i ]++;
}
for( int i = 1 ; i <= n * m ; i++ )
for( int j = 0 ; j < ( 1 << cnt ) ; j++ )
{
dp[ i ][ j ] = 1ll * dp[ i - 1 ][ j ] * max( 0, f[ j ] - i + 1 ) % mod;
for( int k = 1 ; k <= cnt ; k++ )
if( ( j >> k - 1 ) & 1)
upd( dp[ i ][ j ], dp[ i - 1 ][ j ^ ( 1 << k - 1 ) ] );
}
return dp[ n * m ][ ( 1 << cnt ) - 1 ];
}
inline void dfs(int x, int y, int flag)
{
if( y == m + 1 ) { dfs( x + 1, 1, flag ); return ; }
if( x == n + 1 ) { upd( ans, getdp() * flag ); return ; }
dfs( x, y + 1, flag );
if( mp[ x ][ y ] == '.')
{
for( int i = 0 ; i < 8 ; i++ )
{
int nx = x + dx[ i ], ny = y + dy[ i ];
if( !nx || !ny || nx > n || ny > m ) continue;
if( mp[ nx ][ ny ] == 'X' ) return ;
}
mp[ x ][ y ] = 'X';
dfs( x, y + 1, -flag );
mp[ x ][ y ] = '.';
}
}
int main()
{
scanf( "%d%d", &n, &m );
for( int i = 1; i <= n ; i++ ) scanf( "%s", mp[ i ] + 1 );
for( int i = 1; i <= n ; i++ )
for( int j = 1 ; j <= m ; j++ )
if( mp[ i ][ j ] == 'X' )
for( int k = 0 ; k < 8 ; k++ )
{
int nx = i + dx[ k ], ny = j + dy[ k ];
if( !nx || !ny || nx > n || ny > m ) continue;
if( mp[ nx ][ ny ] == 'X' ) return printf( "0\n" ), 0;
}
dfs( 1, 1, 1 );
return printf( "%d\n", ans ), 0;
}