【bzoj 2669】[cqoi2012]局部极小值(状压dp+容斥原理)
2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 552 Solved: 288
[Submit][Status][Discuss]
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+容斥原理】
【这道题,由于状态很多,如果逐一用维数来表示,MLE!】
【所以,考虑状压,因为状压后数不会很大(由题目可知,如果压缩'X'的填数情况,因为'X'最多有8个)】
【按顺序逐一填数,用f[i][j]表示用了前i-1个数,j表示‘X’的填数情况(填了为1,未填为0),然后,当前一步有两种可能:1)向未填的'X'位填数;2)向非'X'位填数,在这种情况下要注意如果与当前格相邻的'X'格未填数,而当前又不向'X'格填数,那么'X'格将要填的数必要大于当前数,所以当前数不能填在当前格。 我们可以预处理每种j所对应的状态下有多少个格可以填数num[j],不过在dp转移的时候,不能直接加num[j],由于当前已用i-1个格,所以加的时候是:max(num[j]-i+1)】
【当我们dp完成后,会发现答案并不正确,原因是当前给出的局部极小值不一定会包含当前网格中所有可能为局部极小值的位置,即有些未标出是局部极小值的位置也有可能在某种填数方案中成为局部极小值,而这是不符合题意的,所以,我们要dfs搜素,用容斥的方法搞掉这些情况:+还有0个局部极小值-还有1个局部极小值+还有2个局部极小值……,每次计算这些的方法就是把当前格置为'X',然后进行dp】
【最后,warning!!!!!注意读入方式,窝已WA挺……QWQ】
[博主已哭死在键盘上ing]
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 12345678
using namespace std;
char ch[5][8];
int d[8][8]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
int f[30][260],a[10][10],X[10][2],num[260],cnt,n,m,N,ans;
bool p[5][8];
inline int find()
{
int i,j,k; cnt=0;
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
if(a[i][j]) X[++cnt][0]=i,X[cnt][1]=j;
for(k=0;k<(1<<cnt);++k)
{
int tot=0;
memset(p,0,sizeof(p));
for(i=1;i<=cnt;++i)
if(!((k>>(i-1))&1))
{
p[X[i][0]][X[i][1]]=1;
for(j=0;j<8;++j)
{
int xx=X[i][0]+d[j][0],yy=X[i][1]+d[j][1];
if(xx<=n&&xx>0&&yy>0&&yy<=m) p[xx][yy]=1;
}
}
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
if(p[i][j]) tot++;
num[k]=N-tot;
}
memset(f,0,sizeof(f));
f[0][0]=1;
for(i=1;i<=N;++i)
for(j=0;j<(1<<cnt);++j)
{
f[i][j]=(f[i][j]+f[i-1][j]*max(num[j]-i+1,0))%mod;
for(k=1;k<=cnt;++k)
if(j&(1<<(k-1))) f[i][j]=(f[i][j]+f[i-1][j^(1<<k-1)])%mod;
}
return f[N][(1<<cnt)-1];
}
void dfs(int x,int y,int t)
{
if(y==m+1)
{
dfs(x+1,1,t);
return;
}
if(x==n+1)
{
if((t%2)) ans-=find()%mod;
else ans=(ans+find())%mod;
ans=(ans%mod+mod)%mod;
return;
}
dfs(x,y+1,t);
bool b=1;
for(int i=0;i<8;++i)
if(a[x+d[i][0]][y+d[i][1]]) {b=0; break; }
if(b&&!a[x][y])
{
a[x][y]=1;
dfs(x,y+1,t+1);
a[x][y]=0;
}
}
int main()
{
freopen("int.txt","r",stdin);
freopen("my.txt","w",stdout);
int i,j;
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%s",ch[i]+1);
for(j=1;j<=m;++j)
if(ch[i][j]=='X') a[i][j]=1;
}
N=n*m;
dfs(1,1,0);
ans=(ans%mod+mod)%mod;
printf("%d\n",ans);
return 0;
}
既然无能更改,又何必枉自寻烦忧