题解 -
[CQOI2012] 局部极小值
- 一道超神状压
DP,总算看懂题解。。。这里来写写自己的心路历程。
题目意思
Sol
- 首先要明白为什么能用状压做这道题目,因为最多只会存在
8个局部最小值(即各一个
′.′放一个
′X′)
- 于是就有
fi,S表示数字填到
i,此时填
′X′状态为
S的方案总数(这还是比较好理解的)
- 然后我们来看如何转移
- 当前数
i填入
′X′中,那么我们就要从已经填过的
′X′转移过来。即
fi,S=∑k∣((1<<k−1)&S==0)fi−1,S xor(1<<k−1)其中
S xor(1<<k−1)表示这个状态
k这个坑没填过,现在要填就累计起来。
- 当不填入
′X′中,我们要先计算出有几种填法。对于那些没有填过的
‘X′周围也不能填。于是就是总数
n∗m减去那些不合法的情况。于是转移就是上一个状态转移过来:
fi,S=fi−1,S×(sum−i+1),
sum−i+1的意思就是一共合法的格子数为
sum减去已经用了的数字
i−1那么就是
sum−(i−1)啦。还是比较好理解的。
- 最后一步就是:我们在填数的时候很可能造成一种情况即:这个点本身不是
′X′而他周围的数都比他大,那么我们肯定要容斥掉这样的情况,这个实现很简单,就是把这些点先当作
′X′,最后再减掉即可。这个应该还是比较显然的。
- 还有记得对于那种相邻
′X′的情况直接输出
0就可以了。
Code
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int sum=0,ff=1; char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=getchar();
return sum*ff;
}
const int N=1<<9|1;
const int mo=12345678;
const int dx[10]={-1,-1,-1,0,0,1,1,1,0};
const int dy[10]={-1,0,1,-1,1,-1,0,1,0};
int n,m,ans,hi[N],f[35][N],vis[15][15];
int px[50],py[50],is[15][15],gs;
char ch[15];
inline int dp()
{
memset(f,0,sizeof(f));
f[0][0]=1;
for ( int i=0;i<(1<<gs);i++ )
{
hi[i]=m*n;
memset(vis,0,sizeof(vis));
for ( int j=1;j<=gs;j++ )
if(!(i&(1<<j-1)))
for ( int k=0;k<9;k++ )
vis[px[j]+dx[k]][py[j]+dy[k]]=1;
for ( int j=1;j<=n;j++ )
for ( int k=1;k<=m;k++ )
hi[i]-=vis[j][k];
}
for ( int i=1;i<=m*n;i++ )
for ( int j=0;j<(1<<gs);j++ )
{
if(hi[j]-i+1>0)
f[i][j]=(f[i][j]+f[i-1][j]*(hi[j]-i+1))%mo;
for ( int k=1;k<=gs;k++ )
if(j&(1<<k-1))
f[i][j]=(f[i][j]+f[i-1][j^(1<<k-1)])%mo;
}
return f[n*m][(1<<gs)-1];
}
inline int dfs(int x,int y)
{
if(y==m+1) x++,y=1;
if(x==n+1) return dp();
int now=dfs(x,y+1);
for ( int i=0;i<9;i++ )
{
int tx=x+dx[i],ty=y+dy[i];
if(is[tx][ty]) return now;
}
is[x][y]=1;
px[++gs]=x,py[gs]=y;
now-=dfs(x,y+1);
now=(now+mo)%mo;
gs--,is[x][y]=0;
return now;
}
int main()
{
n=read();
m=read();
for ( int i=1;i<=n;i++ )
{
scanf("%s",ch+1);
for ( int j=1;j<=m;j++ )
if(ch[j]=='X')
{
px[++gs]=i;
py[gs]=j;
is[i][j]=1;
}
}
for ( int i=1;i<=gs;i++ )
for ( int j=i+1;j<=gs;j++ )
if(abs(px[i]-px[j])<=1&&abs(py[i]-py[j])<=1)
return printf("0\n"),0;
ans=dfs(1,1);
printf("%d\n",ans);
return 0;
}