bzoj 2669: [cqoi2012]局部极小值(dp+容斥原理)
来源:互联网 发布:mac查询端口占用情况 编辑:程序博客网 时间:2024/05/16 05:05
2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 537 Solved: 280
[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 +容斥原理。
这道题的数据范围很小。我们很容易想到状压动归,但是如果我们枚举每一个位置放置什么就一定会存在判断当前数是否已经用过的问题,2^28很显然不能承受。
我们发现局部极小值的点最多8个,所以我们考虑把局部最小值是否已经填过状压成一维,然后考虑从小到大往格子中填数,因为每个数枚举一次所以也就只能填一次。
f[i][j]表示填到数i,局部最小值的填写状态为j。
i数可以填写到的位置可以分成两种,‘x'位置,和非’x'位置。
如果考虑填入‘x'位置,因为是从小到大开始填写,所以直接填就好,因为周围的点要么没填要么填的比这个位置小。我们只需要令j状态中填写的位置k,f[i][j]+=f[i-1][j^(1<<k-1)]
如果考虑填入非‘x’位置,很显然有些位置是不能填的(就是那些未填的'x'位置和他周围的位置),我们可以预处理出每种状态对应的可以填写的位置的个数(即除去那些未填的'x'位置和他周围的位置剩下的位置),但是这些位置中已经填写了(i-1)个数,
所以在递推的时候要减去,即f[i][j]+=f[i-1][j]*max(num[j]-i+1,0)。
但是我们这样算完后发现答案还是不对,为什么呢?因为我们是保证了'x'位置是局部极小值,但是并没有保证'.'的位置不是,局部最小值,所以Ans=多包含至少0个局部极小值-多包含至少1个局部极小值+多包含至少2个局部极小值-...
这个容斥的时候用dfs搜索出那些不是'x'但是有可能成为局部极小值的位置,然后用上面的方式计算一下,在更新最终答案即可。
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define N 30#define p 12345678using namespace std;int f[N][1<<8],n,m,ans;int x[N],y[N],num[1<<8],tot,vis[N][N];int posx[10]={-1,-1,-1,0,0,1,1,1},posy[10]={-1,0,1,-1,1,-1,0,1};char s[N][N];int solve(){tot=0;for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (s[i][j]=='X') x[++tot]=i,y[tot]=j;for (int i=0;i<(1<<tot);i++) { int cnt=0; memset(vis,0,sizeof(vis));for (int j=1;j<=tot;j++) if (!((i>>(j-1))&1)) { vis[x[j]][y[j]]=1; for (int k=0;k<8;k++) { int xx=x[j]+posx[k]; int yy=y[j]+posy[k]; if (xx>0&&yy>0&&xx<=n&&yy<=m) vis[xx][yy]=1; } }for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) if (vis[j][k]) cnt++;num[i]=n*m-cnt; }memset(f,0,sizeof(f));f[0][0]=1;for (int i=1;i<=n*m;i++) for (int j=0;j<(1<<tot);j++) { f[i][j]=(f[i][j]+f[i-1][j]*max(num[j]-i+1,0))%p; for (int k=1;k<=tot;k++) if (j&(1<<(k-1))) f[i][j]=(f[i][j]+f[i-1][j^(1<<k-1)])%p; }return f[n*m][(1<<tot)-1];}void dfs(int x,int y,int k){if (y==m+1) { dfs(x+1,1,k); return; }if (x==n+1) { if (k%2==0) ans=(ans+solve())%p; else ans-=solve()%p; ans=(ans%p+p)%p; return; }dfs(x,y+1,k);bool f=true;for (int i=0;i<8;i++) if (s[x+posx[i]][y+posy[i]]=='X') { f=false; break; }if (f&&s[x][y]!='X'){ s[x][y]='X'; dfs(x,y+1,k+1); s[x][y]='.';}}int main(){freopen("a.in","r",stdin);scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) { scanf("%s",s[i]+1); for(int j=1;j<=m;j++) if (s[i][j]=='X') x[++tot]=i,y[tot]=j;}dfs(1,1,0);printf("%d\n",(ans%p+p)%p);}
0 0
- bzoj 2669: [cqoi2012]局部极小值(dp+容斥原理)
- 【bzoj 2669】[cqoi2012]局部极小值(状压dp+容斥原理)
- [DP 容斥] BZOJ 2669 [cqoi2012]局部极小值
- BZOJ 2669 cqoi2012 局部极小值 状压DP+容斥原理
- BZOJ 2669 cqoi2012 局部极小值 状压DP+容斥原理
- BZOJ 2669: [cqoi2012]局部极小值 状压dp 容斥原理
- [BZOJ2669][cqoi2012][状压DP][容斥原理]局部极小值
- [BZOJ2669][cqoi2012]局部极小值(状压DP+容斥原理)
- CQOI2012局部极小值 BZOJ2669 状压DP+容斥
- BZOJ 2669([cqoi2012]局部极小值-状态压缩+dp)
- bzoj 2669: [cqoi2012]局部极小值
- BZOJ 2669 局部极小值 CQOI2012
- BZOJ 2669- [cqoi2012]局部极小值
- BZOJ 2669: [cqoi2012]局部极小值
- bzoj 2669: [cqoi2012]局部极小值
- 【bzoj2669】【cqoi2012】【局部极小值】【状压dp】
- 【CQOI2012】局部极小值
- bzoj2669: [cqoi2012]局部极小值
- redis 超全的操作
- MVC---Android App的设计架构:MVC,MVP,MVVM与架构经验谈
- MFC连接access数据库的时候读取date类型遇到的问题
- VS2015使用小技巧 winform编辑窗体(拖拽控件)的的时候,工具箱挡住了窗体,如何解决
- 数据库连接池的原理机制
- bzoj 2669: [cqoi2012]局部极小值(dp+容斥原理)
- AE学习笔记之地图符号化
- 外部解决滑动冲突水平方向和竖直方向滑动冲突实例
- OAuth2.0大致模型介绍
- 剩余的文件数
- 400. Nth Digit
- Leetcode #312 Burst Balloons
- CAP协议字符串签名
- mysql binlog row格式查看