题目链接
P3160 [CQOI2012]局部极小值
双倍经验,双倍快乐
解题思路
存下来每个坑(极小值点)的位置,以这个序号进行状态压缩。
显然,\(4*7\)的数据范围让极小值点在8个以内(以下示意)
X . X . X . X .
. . . . . . . .
X . X . X . X .
. . . . . . . .
所以考虑用\(S\)表示各个极小值点是否已填的状态,枚举\(1-n*m\)进行状压\(DP\)。
当前填的数有两种选择:
(\(1\))填入坑中,这样枚举\(S\)状态表示的每一个已填的坑,\(f[i][S]+=\)\(\sum_{k|((1<
(\(2\))不填入坑中,这样枚举每一个点,如果可以填(指如果有坑还没填,那么他旁边以及他本身共\(9\)个格都不能填东西,否则这个坑继续往后填填不成极小值),那么就是一种新的填法,数出来\(tot\)种填法,那么\(dp[i][S]+=dp[i-1][S]*(tot-i+1)\)。然后发现枚举不是很好,可以进行预处理,处理出每一个状态对应的\(tot\)(代码中用\(hi[S]\)表示)。
然后答案就是\(f[m*n][(1<
然后因为是胡乱填数,于是可能会出现不应该是坑的地方变成坑的情况,于是就在每个可能出现坑的地方分别新加一个坑进行\(DP\),完了再减去。而这个\(DP\)又带来新的可能填的坑......而这就是个容斥问题了。
AC代码
#include
#include
#include
char a[10];
int m,n,min[6][10];//描述整个矩阵
int num,x[30],y[30];//描述坑的个数、位置
int w=12345678;//膜
int dx[10]={-1,-1,-1,0,0,1,1,1,0};//移动位置
int dy[10]={-1,0,1,-1,1,-1,0,1,0};
int vis[6][10],f[29][(1<<8)+10],hi[1<<9];//dp用到的东西
int dp(){
int i,j,k;
memset(f,0,sizeof(f));
f[0][0]=1;
for(i=0;i<(1<0)f[i][j]+=f[i-1][j]*(hi[j]-i+1),f[i][j]%=w;
for(k=0;k