bzoj 2669 题解(状压dp+搜索+容斥原理)
这题太难了...看了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+搜索+容斥原理)的更多相关文章
- BZOJ 2064: 分裂( 状压dp )
n1+n2次一定可以满足..然后假如之前土地集合S1的子集subs1和之后土地集合S2的子集subs2相等的话...那么就少了2个+操作...所以最后答案就是n1+n2-少掉的最多操作数, 由状压dp ...
- O - Matching 题解(状压dp)
题目链接 题目大意 给你一个方形矩阵mp,边长为n(n<=21) 有n个男生和女生,如果\(mp[i][j]=1\) 代表第i个男生可以和第j个女生配对 问有多少种两两配对的方式,使得所有男生和 ...
- [BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)
[BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关 ...
- 【Gym100837F】Controlled Tournament(状压Dp 搜索剪枝)
题目链接 大意 现有\(N\)个人要打比赛,知道任意两个人间打比赛的胜负关系. 要求在 深度最小 的情况下,根为\(M\)的 竞赛树 的个数. 满足\(1\le M\le N\le 16\) 思路 虑 ...
- 【BZOJ2560】串珠子(状压DP,容斥原理)
题意: 铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子.现在铭铭想用绳子把所有的珠子连接成一个整体.现在已知所有珠子互不相同,用整数1到n编号.对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在 ...
- BZOJ 3812 主旋律 (状压DP+容斥) + NOIP模拟赛 巨神兵(obelisk)(状压DP)
这道题跟另一道题很像,先看看那道题吧 巨神兵(obelisk) 题面 欧贝利斯克的巨神兵很喜欢有向图,有一天他找到了一张nnn个点mmm条边的有向图.欧贝利斯克认为一个没有环的有向图是优美的,请问这张 ...
- 【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理
题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum ...
- 【bzoj2560】串珠子 状压dp+容斥原理
题目描述 有 $n$ 个点,点 $i$ 和点 $j$ 之间可以连 $0\sim c_{i,j}$ 条无向边.求连成一张无向连通图的方案数模 $10^9+7$ .两个方案不同,当且仅当:存在点对 $(i ...
- NOIP2017 宝藏 题解报告【状压dp】
题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...
随机推荐
- java 写 Excel(不生成实体文件,写为流的形式)
java 写 Excel(不生成实体文件,写为流的形式) public String exportReportExcel(String mediaCode, List<SimpleMediaRe ...
- 关于setInterval返回值问题
oBtn1.onclick = function(){ clearInterval(timer); timer = setInterval(cwidth,10); alert(timer); } oB ...
- python基本数据结构栈stack和队列queue
1,栈,后进先出,多用于反转 Python里面实现栈,就是把list包装成一个类,再添加一些方法作为栈的基本操作. 栈的实现: class Stack(object): #初始化栈为空列表 def _ ...
- scrapy基础 之 xpath网页结构
1 ,什么是xpath XPath 是一门在 XML 文档中查找信息的语言.XML是一种类似于HTML的传输协议 2,节点 XPath 中,有七种类型的节点:元素.属性.文本.命名空间.处理指令.注释 ...
- 【转载】奇异值分解(SVD)计算过程示例
原文链接:奇异值分解(SVD)的计算方法 奇异值分解是线性代数中一种重要的矩阵分解方法,这篇文章通过一个具体的例子来说明如何对一个矩阵A进行奇异值分解. 首先,对于一个m*n的矩阵,如果存在正交矩阵U ...
- HDOJ 4267 A Simple Problem with Integers (线段树)
题目: Problem Description Let A1, A2, ... , AN be N elements. You need to deal with two kinds of opera ...
- python进程.线程和协程的总结
I.进程: II.多线程threading总结 threading用于提供线程相关的操作,线程是应用系统中工作的最小单位(cpu调用的最小单位). Python当前版本的多线程没有实现优先级,线程组, ...
- DAC杂谈一
DAC种类: 有权电阻网络DAC 输出电压变化范围为:0~-(2^n-1)/2^n*Vref 当位数很高时,每个电阻都有很高精度是十分困难的. 倒T型电阻网络DAC(比如AD7520 10bit 已停 ...
- 修改Linux主机名与IP之间的映射关系
linux主机版本: Distributor ID: UbuntuDescription: Ubuntu 14.10Release: 14.10 一.修改linux主机名 1.使用hostname命令 ...
- 【翻译】Chemkin - Chapter 1
[这段文字是2013年3月翻译的,整理老文档时发现就拿出来,提醒我们:任何试图翻译英文手册的尝试都是徒劳的] 第一章 介绍 Chemkin理论手册提供了一个对关系和公式的宽泛的概览,这些关系和公式 ...