这题太难了...看了30篇题解才整明白到底咋回事...

核心思想:状压dp+搜索+容斥

首先我们分析一下,对于一个4*7的棋盘,低点的个数至多只有8个(可以数一数)

这样的话,我们可以进行一个状压,把所有的低点压进来

然后我们从小到大枚举所有数,转移即可

记状态f[i][j]表示到了第i个数,低点的状态为j的方案数

那么在转移的时候,有两个转移方向:

①.如果第i个数放在低点上,那么我们可以枚举所有的低点k,如果低点没有在状态里,有:

dp[i][j|(1<<k)]+=dp[i-1][j]

②.如果第i个数放在高点上,那么我们需要枚举所有可以使用的高点,所谓可以使用的高点就是指的某一高点周围没有未使用的低点(原因:我是从小往大枚举的所有数,如果我在一个高点上放了一个数而他附近却有低点没有放上,那么这个低点会变得比这个高点高,这就是不合法的了。)

但是如果每次都枚举,时间复杂度是会爆炸的,所以我们需要进行一个预处理num[i],表示i状态下有多少个高点可以使用

最后的答案就是dp[n*m][1<<cnt-1]

可是这个答案是正确的吗?

我们发现,如果仅仅是这么枚举,很容易出现一种情况:某个点本来是高点,但是由于随意的放置使得这个点变成了低点

所以我们需要解决掉这个问题

怎么解决?

很显然,我们只需求出把每个可能被放错的高点真正作为低点的方案数,然后用总方案数减掉这个方案数就可以了。

可是,如果我们分别去减,由于减的方案数还要像上面那样dp出来,所以会产生更多的重复(即高点1和高点2同时放错)

所以我们需要利用容斥,即

0个高点反转-1个高点反转+2个高点反转....

(反转指反转状态)

那么怎么反转?

dfs!

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define mode 772002
using namespace std;
bool used[10][10];
bool maps[10][10];
int n,m;
ll dp[30][(1<<15)+5];
char ch[10];
int dir[10][2]={{0,0},{1,1},{1,0},{1,-1},{0,1},{0,-1},{-1,1},{-1,0},{-1,-1}};
int temp[30][2];
int num[(1<<15)+5];
ll ans=0;
bool check(int x,int y)
{
if(x>0&&x<=n&&y>0&&y<=m)
{
return 1;
}
return 0;
}
ll get_dp()
{
int cnt=0;
memset(dp,0,sizeof(dp));
memset(used,0,sizeof(used));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(maps[i][j])
{
used[i][j]=1;
temp[++cnt][0]=i;
temp[cnt][1]=j;
}
}
}
for(int i=0;i<=(1<<cnt)-1;i++)
{
int tot=0;
memset(used,0,sizeof(used));
for(int j=1;j<=cnt;j++)
{
if(!((1<<(j-1))&i))
{
if(!used[temp[j][0]][temp[j][1]])
{
used[temp[j][0]][temp[j][1]]=1;
tot++;
}
for(int t=1;t<=8;t++)
{
int x=temp[j][0]+dir[t][0];
int y=temp[j][1]+dir[t][1];
if(check(x,y)&&!used[x][y])
{
used[x][y]=1;
tot++;
}
}
}
}
num[i]=n*m-tot;
}
dp[0][0]=1;
for(int i=1;i<=n*m;i++)
{
for(int j=0;j<=(1<<cnt)-1;j++)
{
dp[i][j]+=dp[i-1][j]*max(num[j]-i+1,0)%mode;
dp[i][j]%=mode;
for(int k=1;k<=cnt;k++)
{
if(!((1<<(k-1))&j))
{
dp[i][j|(1<<(k-1))]+=dp[i-1][j];
dp[i][j|(1<<(k-1))]%=mode;
}
}
}
}
return dp[n*m][(1<<cnt)-1];
}
void dfs(int x,int y,int typ)
{
if(x==n+1)
{
if(typ%2)
{
ans-=get_dp();
ans%=mode;
}else
{
ans+=get_dp();
ans%=mode;
}
return;
}
if(y==m+1)
{
dfs(x+1,1,typ);
return;
}
dfs(x,y+1,typ);
if(!maps[x][y])
{
bool flag=0;
for(int i=1;i<=8;i++)
{
int st=x+dir[i][0];
int ed=y+dir[i][1];
if(maps[st][ed])
{
flag=1;
break;
}
}
if(!flag)
{
maps[x][y]=1;
dfs(x,y+1,typ+1);
maps[x][y]=0;
return;
}
}
}
int main()
{
int cot=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
cot++;
bool u=0;
memset(maps,0,sizeof(maps));
for(int i=1;i<=n;i++)
{
scanf("%s",ch);
for(int j=0;j<m;j++)
{
if(ch[j]=='.')
{
maps[i][j+1]=0;
}else
{
maps[i][j+1]=1;
if(maps[i-1][j+1]||maps[i][j])
{
u=1;
}
}
}
}
if(u)
{
printf("Case #%d: 0\n",cot);
continue;
}
ans=0;
dfs(1,1,0);
printf("Case #%d: %lld\n",cot,(ans%mode+mode)%mode);
}
return 0;
}

bzoj 2669 题解(状压dp+搜索+容斥原理)的更多相关文章

  1. BZOJ 2064: 分裂( 状压dp )

    n1+n2次一定可以满足..然后假如之前土地集合S1的子集subs1和之后土地集合S2的子集subs2相等的话...那么就少了2个+操作...所以最后答案就是n1+n2-少掉的最多操作数, 由状压dp ...

  2. O - Matching 题解(状压dp)

    题目链接 题目大意 给你一个方形矩阵mp,边长为n(n<=21) 有n个男生和女生,如果\(mp[i][j]=1\) 代表第i个男生可以和第j个女生配对 问有多少种两两配对的方式,使得所有男生和 ...

  3. [BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)

    [BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关 ...

  4. 【Gym100837F】Controlled Tournament(状压Dp 搜索剪枝)

    题目链接 大意 现有\(N\)个人要打比赛,知道任意两个人间打比赛的胜负关系. 要求在 深度最小 的情况下,根为\(M\)的 竞赛树 的个数. 满足\(1\le M\le N\le 16\) 思路 虑 ...

  5. 【BZOJ2560】串珠子(状压DP,容斥原理)

    题意: 铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子.现在铭铭想用绳子把所有的珠子连接成一个整体.现在已知所有珠子互不相同,用整数1到n编号.对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在 ...

  6. BZOJ 3812 主旋律 (状压DP+容斥) + NOIP模拟赛 巨神兵(obelisk)(状压DP)

    这道题跟另一道题很像,先看看那道题吧 巨神兵(obelisk) 题面 欧贝利斯克的巨神兵很喜欢有向图,有一天他找到了一张nnn个点mmm条边的有向图.欧贝利斯克认为一个没有环的有向图是优美的,请问这张 ...

  7. 【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理

    题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum ...

  8. 【bzoj2560】串珠子 状压dp+容斥原理

    题目描述 有 $n$ 个点,点 $i$ 和点 $j$ 之间可以连 $0\sim c_{i,j}$ 条无向边.求连成一张无向连通图的方案数模 $10^9+7$ .两个方案不同,当且仅当:存在点对 $(i ...

  9. NOIP2017 宝藏 题解报告【状压dp】

    题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...

随机推荐

  1. git 新建分支

    创建分支   git checkout -b 分支名 推送到远程   git push origin 分支名

  2. eclipse使用异常An error has occurred.see error log for more details eclipse

    eclipse使用异常An error has occurred.see error log for more details eclipse 解决Eclipse,MyEclipse出现An erro ...

  3. Django中模板语音变量forloop

    forloop.counter   从1开始 forloop.counter0 从0开始 forloop.revcounter  倒序(表示循环中剩余项的整型变量.) forloop.revcount ...

  4. python读取两个文件并且判断是否一致

    ''' 判断两个文件是否相同,如果不同请指出第几行不相同 ''' def f1vsf2(name1,name2): f1 = open(name1) f2 = open(name2) count = ...

  5. CentOS 7 安装配置 MySQL

    https://blog.imzhengfei.com/centos-7-an-zhuang-pei-zhi-mysql/ MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前 ...

  6. 方法join()使用详解

    在线程的常见方法一节中,已经接触过join()方法的使用. 在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将早于子线程结束.这时,如果主线程想等子线程执行完成才结束,比 ...

  7. eclipse连接手机一直连接不起

    任何环境都搭配没有问题,依然连接不起手机,最后解决办法: 把sdk下tools的文件全部复制一份到platform-tools下即可

  8. CF1139A Even Substrings

    题目地址:CF1139A Even Substrings 一个数是偶数等价于其最后一位为偶数(2/4/6/8/0) 从左往右扫一遍,如果一个数是奇数则直接跳过,偶数则加上它对答案的贡献 这里的贡献应该 ...

  9. Thymeleaf模板布局

    ⒈定义片段 1.使用th:fragment <div th:fragment="copy"> © 2019 <a href="http://www.co ...

  10. vue中过滤器filters的使用

    组件内写法 filters:{ filter:function(data,arg1,arg2){ return .... } } 全局写法 filters('filter',function(data ...