[CQOI2012]局部极小值 DP+容斥

版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/82763789

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


Sample Input
3 2
X.

.X


Sample Output
60


有点奇妙…
首先数据范围小第一眼想到暴搜。
然后你可以发现最多只有8个X。
然后呢,就是一个DP (又被踩爆)
你设f[i][j]为填到第i个数,已知X的状态。
对于X你可以直接填,非X你要他周围的X都确定了才可填,然后你就转移即可。
然后你发现这样肯定是错的,于是你要容斥一下,因为最多只有8个X,大胆暴搜。


#include <cstdio>
#include <cstring>

using namespace std;
const int mod = 12345678;
const int dx[8] = {0, 1, 0, -1, 1, 1, -1, -1};
const int dy[8] = {1, 0, -1, 0, 1, -1, 1, -1};

char ss[10];
int id[5][10], num[1100], hh;
int ans, n, m, a[5][10], f[30][1100];

void dfs(int x, int y, int K) {
	if(y == m + 1) y = 1, x++;
	if(x == n + 1) {
		memset(f, 0, sizeof(f));
		memset(id, 0, sizeof(id));
		memset(num, 0, sizeof(num));
		f[0][0] = 1; int cnt = 0;
		for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(a[i][j]) {
			id[i][j] = ++cnt;
		}
		for(int i = 0; i < (1 << cnt); i++) {
			for(int ii = 1; ii <= n; ii++) {
				for(int jj = 1; jj <= m; jj++) {
					if(a[ii][jj]) {
						if((i >> id[ii][jj] - 1) & 1) num[i]++;
					}
					else {
						bool bk = 0;
						for(int k = 0; k < 8; k++) {
							int nx = ii + dx[k], ny = jj + dy[k];
							if(nx <= 0 || ny <= 0 || nx > n || ny > m) continue;
							if(a[nx][ny] && !((i >> id[nx][ny] - 1) & 1)) bk = 1;
						} if(!bk) num[i]++;
					}
				}
			}
		}
		for(int i = 1; i <= n * m; i++) {
			for(int j = 0; j < (1 << cnt); j++) if(f[i - 1][j]){
				(f[i][j] += f[i - 1][j] * (num[j] - i + 1) % mod) %= mod;
				for(int k = 0; k < cnt; k++) if(!(j >> k & 1)) {
					(f[i][j | (1 << k)] += f[i - 1][j]) %= mod;
				}
			}
		} if(K % 2 == 0) (ans += f[n * m][(1 << cnt) - 1]) %= mod, hh = f[n * m][(1 << cnt) - 1];
		else (ans -= f[n * m][(1 << cnt) - 1]) %= mod;
		return ;
	}
	if(a[x][y]) {dfs(x, y + 1, K); return ;}
	bool bk = 0;
	for(int k = 0; k < 8; k++) {
		int nx = x + dx[k], ny = y + dy[k];
		if(nx <= 0 || ny <= 0 || nx > n || ny > m) continue;
		if(a[nx][ny]) {bk = 1; break;}
	} if(!bk) a[x][y] = 1, dfs(x, y + 1, K + 1), a[x][y] = 0;
	dfs(x, y + 1, K);
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%s", ss + 1);
		for(int j = 1; j <= m; j++) if(ss[j] == 'X'){
			a[i][j] = 1;
		}
	} dfs(1, 1, 0);
	printf("%d\n", (ans + mod) % mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/82763789