HDU5838 Mountain(状压DP + 容斥原理)
题目
Source
http://acm.hdu.edu.cn/showproblem.php?pid=5838
Description
Zhu found a map which is a N∗M rectangular grid.Each cell has a height and there are no two cells which have the same height. But this map is too old to get the clear information,so Zhu only knows cells which are valleys.
A cell is called valley only if its height is less than the heights of all its adjacent cells.If two cells share a side or a corner they are called adjacent.And one cell will have eight adjacent cells at most.
Now give you N strings,and each string will contain M characters.Each character will be '.' or uppercase 'X'.The j-th character of the i-th string is 'X' if the j-th cell of the i-th row in the mountain map is a valley, and '.' otherwise.Zhu wants you to calculate the number of distinct mountain maps that match these strings.
To make this problem easier,Zhu defines that the heights are integers between 1 and N∗M.Please output the result modulo 772002.
Input
The input consists of multiple test cases.
Each test case begins with a line containing two non-negative integers N and M. Then N lines follow, each contains a string which contains M characters. (1≤N≤5,1≤M≤5).
Output
For each test case, output a single line "Case #x: y", where x is the case number, starting from 1. And y is the answer after module 772002.
Sample Input
2 4
.X..
...X
4 2
X.
..
..
X.
1 2
XX
Sample Output
Case #1: 2100
Case #2: 2520
Case #3: 0
分析
题目大概说给一张大小n*m由'X'或'.'标记的图,要在其各个位置填入1到n*m这几个数,满足'X'填入的数是极小的(小于周围八个数)且'.'填入的数不是极小的,求有几种填法。
好难的感觉。。
考虑把数从小到大依次填入:
- dp[i][S]表示当前已经填入数字1...i,且已经被填数的'X'集合为S的方案数(因为合法的'X'最多有9个所以用9位二进制压缩表示S即可)
通过将i+1填入转移到dp[i+1]的状态:
- 可以选择把i+1填入下一个还没被填的'X'位置x
- 也可以填入'.'的位置,这个点'.'的位置不能是未填的'X'周围,因为是从小到大填数的
这样子得到的方案数得到的不是答案要的方案数,因为它还包含了'.'填入的数是极小的,这个用容斥原理搞一下,具体就是用一个dfs搜出所有合法的'.'变为'X'的情况,奇数个减,偶数个加。。
代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m; int dx[]={0,0,1,-1,1,-1,1,-1}; int dy[]={1,-1,0,0,1,-1,-1,1}; char map[5][5]; long long d[26][1<<9]; int pos[1<<9],siz[1<<9]; long long dp(){ int cnt=0,x[9],y[9]; for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ if(map[i][j]=='X'){ x[cnt]=i; y[cnt]=j; ++cnt; } } } memset(siz,0,sizeof(siz)); for(int s=0; s<(1<<cnt); ++s){ for(int i=0; i<cnt; ++i){ if(s>>i&1){ map[x[i]][y[i]]='Y'; ++siz[s]; } } int res=0; for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ if(map[i][j]!='.') continue; bool flag=1; for(int k=0; k<8; ++k){ int nx=i+dx[k],ny=j+dy[k]; if(nx<0 || nx>=n || ny<0 ||ny>=m) continue; if(map[nx][ny]=='X'){ flag=0; break; } } if(flag) ++res; } } pos[s]=res; for(int i=0; i<cnt; ++i){ if(s>>i&1){ map[x[i]][y[i]]='X'; } } } memset(d,0,sizeof(d)); d[0][0]=1; for(int i=0; i<n*m; ++i){ for(int j=0; j<(1<<cnt); ++j){ if(d[i][j]==0) continue; for(int k=0; k<cnt; ++k){ if(j>>k&1) continue; d[i+1][j^(1<<k)]+=d[i][j]; d[i+1][j^(1<<k)]%=772002; } d[i+1][j]+=d[i][j]*(pos[j]-i+siz[j]); d[i+1][j]%=772002; } } return d[n*m][(1<<cnt)-1]; } long long ans; void dfs(int z,int k){ if(z==n*m){ if(k&1) ans-=dp(); else ans+=dp(); ans%=772002; return; } int x=z/m,y=z%m; bool flag=(map[x][y]!='X'); for(int i=0; i<8; ++i){ int nx=x+dx[i],ny=y+dy[i]; if(nx<0 || nx>=n || ny<0 || ny>=m) continue; if(map[nx][ny]=='X'){ flag=0; break; } } if(flag){ map[x][y]='X'; dfs(z+1,k+1); map[x][y]='.'; } dfs(z+1,k); } bool check(){ for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ if(map[i][j]=='.') continue; for(int k=0; k<8; ++k){ int nx=i+dx[k],ny=j+dy[k]; if(nx<0 || nx>=n || ny<0 || ny>=m) continue; if(map[nx][ny]=='X') return 0; } } } return 1; } int main(){ int cse=0; while(~scanf("%d%d",&n,&m)){ for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ scanf(" %c",&map[i][j]); } } if(!check()){ printf("Case #%d: 0\n",++cse); continue; } ans=0; dfs(0,0); if(ans<0) ans+=772002; printf("Case #%d: %lld\n",++cse,ans); } return 0; }