题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=2669

题解:

容斥,DP,DFS

先看看 dp 部分:
首先呢,X的个数不会超过 8个
个数很少,所以考虑状压,把需要填 X的那几个位置状压为二进制10表示对应的那个X位置是否已经填数。
同时填的数互不重复,考虑从小填到大。
cnt[S] 表示除了不在集合 S 里的 X 位置及其周围的位置,剩下的位置个数
定义 dp[i][S]表示从小到大填数填完了i这个数,且已经填了的 S 这个集合里的 X 位置的方案数
转移:依次去填数 1~N*M,每次有两种选择:
1).把这个数填在 某个 X 位置(枚举一个 k表示第 k个 X 位置填当前数)
dp[i][S]+=dp[i-1][S^(1<<(k-1))]

2).把这个数填在非 X 位置,那么填的位置有 cnt[S]-(i-1) 种。
dp[i][s]+=dp[i-1][s]*(cnt[s]-(i-1)) (好好理解一下这个转移)

这样 dp 可以保证那些给出的 X 位置一定是局部最小值,
因为第二种转移的填数位置都不能填在还没有填数的 X 位置的周围。
所以就完了么?
当然还没有,尽管我们保证了给出的 X 位置一定是局部最小值,
但是没有保证非 X位置一定不是非局部最小值。即,求出来的 dp[N*M][all_S(全集)]的意思是至少all_S集合里的 X位置为局部最小值的方案数
所以容斥如下:
ANS = 至少多填了0个局部最小值的方案数(dp[N*M][all_S])
          -至少多填了1个局部最小值的方案数
         +至少多填了2个局部最小值的方案数
          -....+ ....
这些用于容斥的方案数的求法:
DFS 搜索出哪些非 X 位置还可以改为 X ,
然后对于每一种新的填法,去跑一遍上述的dp即可求得对应的方案数。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define _ % mod
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
const int mv[9][2]={{0,0},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
const int mod=12345678;
char mp[10][10];
int N,M,ANS;
int solve(){
static bool vis[10][10];
static int dp[30][1<<8],cnt[1<<8],x[10],y[10],tot,tmp;
tot=0; memset(dp,0,sizeof(dp));
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++) if(mp[i][j]=='X')
tot++,x[tot]=i,y[tot]=j;
for(int s=0;s<1<<tot;s++){
tmp=0; memset(vis,0,sizeof(vis));
for(int i=1;i<=tot;i++) if(!(s&(1<<(i-1))))
for(int k=0;k<9;k++)
vis[x[i]+mv[k][0]][y[i]+mv[k][1]]=1;
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
if(!vis[i][j]) tmp++;
cnt[s]=tmp;
}
dp[0][0]=1;
for(int i=1;i<=N*M;i++)
for(int s=0;s<1<<tot;s++){
dp[i][s]=(1ll*dp[i][s]+1ll*dp[i-1][s]*max(cnt[s]-(i-1),0)_)_;
for(int k=1;k<=tot;k++) if(s&(1<<(k-1)))
dp[i][s]=(1ll*dp[i][s]+dp[i-1][s^(1<<(k-1))])_;
}
return dp[N*M][(1<<tot)-1];
}
void dfs(int x,int y,int t){
if(y==M+1){dfs(x+1,1,t);return;}
if(x==N+1){
int tmp=solve();
if(t&1) tmp=(-1ll*tmp+mod)_;
ANS=((1ll*ANS+tmp)_+mod)_;
return;
}
dfs(x,y+1,t);
bool fg=1;
for(int k=0;k<9;k++)
if(mp[x+mv[k][0]][y+mv[k][1]]=='X') fg=0;
if(fg){
mp[x][y]='X';
dfs(x,y+1,t+1);
mp[x][y]='.';
}
}
int main()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%s",mp[i]+1);
dfs(1,1,0);
printf("%d",ANS);
return 0;
}

●BZOJ 2669 [cqoi2012]局部极小值的更多相关文章

  1. bzoj 2669 [cqoi2012]局部极小值 DP+容斥

    2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 838  Solved: 444[Submit][Status ...

  2. BZOJ 2669 CQOI2012 局部极小值 状压dp+容斥原理

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2669 题意概述:实际上原题意很简洁了我就不写了吧.... 二话不说先观察一下性质,首先棋盘 ...

  3. 【BZOJ 2669】 2669: [cqoi2012]局部极小值 (状压DP+容斥原理)

    2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 667  Solved: 350 Description 有一 ...

  4. bzoj2669[cqoi2012]局部极小值 容斥+状压dp

    2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 774  Solved: 411[Submit][Status ...

  5. [BZOJ2669] [cqoi2012]局部极小值

    [BZOJ2669] [cqoi2012]局部极小值 Description 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点) ...

  6. P3160 [CQOI2012]局部极小值

    题目 P3160 [CQOI2012]局部极小值 一眼就是状压,接下来就不知道了\(qwq\) 做法 我们能手玩出局部小值最多差不多是\(8,9\)个的样子,\(dp_{i,j}\)为填满\(1~i\ ...

  7. P3160 [CQOI2012]局部极小值 题解(状压DP+容斥)

    题目链接 P3160 [CQOI2012]局部极小值 双倍经验,双倍快乐 解题思路 存下来每个坑(极小值点)的位置,以这个序号进行状态压缩. 显然,\(4*7\)的数据范围让极小值点在8个以内(以下示 ...

  8. BZOJ 2669 Luogu P3160 [CQOI2012]局部极小值 (容斥原理、DP)

    题目链接 (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2669 (luogu) https://www.luogu.org/prob ...

  9. BZOJ 2669 【CQOI2012】 局部极小值

    题目链接:局部极小值 这是一道\(dp\)好题. 由于需要保证某些位置比周围都要小,那么我们可以从小到大把每个数依次填入,保证每个局部极小值填入之前周围都不能填,就只需要在加入的时候计数了. 由于局部 ...

随机推荐

  1. GitHub 上下载单个文件夹

    写代码的一定经常去github上查看.下载一些源码,有时候会想下载一个项目中的一个文件夹里的内容,但是github上只提供了整个项目的下载,而整个项目里东西太多,压缩的文件太大,github的下载速度 ...

  2. ubuntu启动报/root/.profile mesg:ttyname failed错误的解决办法

    修改/root/.profile文件,如下命令 sudo gedit /root/profile 将文中的最后一行mesg n修改成tty -s && mesg n

  3. Python内置函数(39)——help

    英文文档: help([object]) Invoke the built-in help system. (This function is intended for interactive use ...

  4. 19届华为实习生笔试之判断iPv6地址类型

    题二: 答案: #coding=utf-8 import re,sys str = sys.stdin.readline().strip() def regex(str): result = &quo ...

  5. 访问器属性:setter()函数和getter()函数

    1.干嘛用的? getter()函数:返回有效的值 setter()函数:调用它并传入数据,这个函数决定如何处理数据 2.具备哪些属性?如何定义? configurable(默认为true),enum ...

  6. Mysql变量列表

    变量表解释 (https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html)

  7. 复习HTML+CSS(3)

    n  超级链接 l  语法格式:<a 属性 = "值">---</a> l  常用属性: n  Href:目标文件的地址URL,该URL可以是相对地址,也可 ...

  8. 归档(NSKeyedArchiver)的使用

    归档的使用,是归于使用保存数据,但是一些简单的数据,如数组,字典等基本的数据类型,往往不使用在归档中,归档和plist以及UserDefaults最大的区别就在于,前者可以存放自定义的数据类型,而后两 ...

  9. Hibernate(一):安装hibernate插件到eclipse环境

    离线安装hibernate插件到eclipse 为什么需要安装hibernate插件到eclipse?在开发eclipse时,很多配置文件信息如果有了hibernate插件集成进来就会有自能提示,方便 ...

  10. 基于OpenCV单目相机的快速标定--源码、工程、实现过程

    相机的标定是所有人走进视觉世界需要做的第一件事,辣么多的视觉标定原理解释你可以随便在网上找到,这里只讲到底如何去实现,也算是给刚入门的朋友做个简单的分享. 1.单目相机标定的工程源码 首先请到同性交友 ...