题意:有一个n*m的矩阵,其中部分格子已经涂黑,部分涂白,要求为其他格子也上黑/白色,问有多少种涂法可以满足一下要求:

  (1)任意2*2的子矩阵不可以同色。

  (2)所有格子必须上色。

  (3)只能有两个连通分量(即1黑1白)。

  注:1<n,m<9。若能满足,顺便输出任一种涂色方法。

思路:  

  本来题也不难,只是刚开始写最小表示法,加上这题的难度,所以搞非常久。注:本题用最小表示法比较好。

  大概逻辑如下:

    if g[i][j]非首列且2*2子矩阵同色  then  非法状态;

    if 上格连通分量不会丢失.  then  合格状态;

  然后对矩阵最后的两个格子特判如下(合法状态指的是对当前格子合法):

  1)倒数第2格子的判断:

    (1)if g[i][j]与上格同色  then  合法状态;

    (2) if 上格的连通分量编号在轮廓线上并不是唯一  then  合法状态;

    (3) if 轮廓线上所有格子颜色相同  then  合法状态;

  2)倒数第1格子的判断:

    (1)if 先尝试上面3种情况,若合法,再继续。

    (2)if n=2 or 最后一个格子与上/左格(任一个以上都行)连通  then  合法状态;

  当扫完所有格子后,将所有合法中连通分量最多为2个的都是符合要求的。路径记录只需要用一个64位整型来记录即可,较简单。

 #include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
#define ULL unsigned long long
using namespace std;
const int N=;
int g[N][N], code[N],color[N], cur, n, m;
int num[N]; //用于最小表示法
char str[N][N]; //用于路径输出
struct Hash_Map
{
static const int mod=;
static const int NN=;
int head[mod]; //桶指针
int next[NN]; //记录链的信息
LL status[NN]; //状态
LL value[NN]; //状态对应的DP值
ULL path[NN]; //路径的状态
int size; void clear()
{
memset(head, -, sizeof(head));
size = ;
} void insert(LL st, LL val,ULL p)
{
int h = st%mod;
for(int i=head[h]; i!=-; i=next[i])
{
if(status[i] == st)
{
value[i] += val;
path[i] = p; //任一路径
return ;
}
}
status[size]= st;
value[size] = val;
path[size] = p;
next[size] = head[h] ;
head[h] = size++;
}
}hashmap[]; LL encode() //编码
{
memset(num,-,sizeof(num));
LL s=;
for(int i=m-,cnt=; i>=; i--)
{
if(num[code[i]]<) num[code[i]]=cnt++;
s<<=;
s+=(num[code[i]]&)+color[i]*; //最高位为颜色,低3位为轮廓线
}
s<<=;
s+=color[m]; //color[m]放在最低位,方便处理。
return s;
} void decode(LL s) //解码:1个颜色位+3个状态位表示一个格子状态
{
color[m+]=(s&);s>>=;
for(int i=; i<=m; i++) //解码到code[1~m]上面,就不用移动。
{
code[i]=(s&); s>>=;
color[i]=(s&); s>>=;
}
} inline bool isunique() //判断上格子的连通分量是否唯一。
{
for(int i=; i<m; i++) if( code[i]==code[m] ) return false;
return true;
}
inline bool samecolor() //轮廓线上除了color[m]外,所有格子是否同色
{
for(int i=; i<m; i++) if(color[i]!=color[i-]) return false;
return true;
} void comb(int c,int j) //合并连通分量
{
if( c==color[m] ) code[]=code[m]; //与上同色
else code[]=;
if(j> && c==color[] ) //与左同色
{
int t=code[];
for(int i=; i<m; i++) if( code[i]==t ) code[i]=code[];
}
} void trycover(int c,LL v,ULL p,int i,int j) //尝试对g[i,j]涂c色
{
if( j> && color[]==c && color[m+]==c && color[m]==c ) return ; //2*2同色
color[]=c&;
if(i+==n && j+==m) //最后2格特处理
{
if( c==color[m] || !isunique() || samecolor() )
{
comb(c,j);
hashmap[cur].insert(encode(), v, p);
}
}
else if(i+==n && j+==m)//最后1格特处理
{
if( c==color[m] || !isunique() || samecolor() )
{
if(i<|| c==color[m]||c==color[]) //如果是m=2的矩阵,需要特别注意最后两个格子。
{
comb(c,j);
hashmap[cur].insert(encode(), v, p);
}
}
}
else
{
if( c!=color[m] && isunique() ) return ; //连通分量消失
comb(c,j);
hashmap[cur].insert(encode(), v, p);
}
} void cal()
{
hashmap[cur=].clear();
for(int i=; i<(<<m); i++ ) //产生第1行的所有情况。
{
color[m]=;
bool flag=;
for(int j=,t=i; j<m; j++,t>>=) //判断是否冲突。
{
color[j]=(t&);
if(g[][m--j]< && g[][m--j]!=color[j]) flag=; //注意点
}
if(flag)
{
code[m-]=;
for(int j=m-,up=; j>=; j--)
if(color[j]==color[j+]) code[j]=code[j+];
else code[j]=++up;
hashmap[].insert(encode(), , i);
}
} for(int i=; i<n; i++) //穷举剩下所有格子。
{
for(int j=; j<m; j++)
{
cur^=;
hashmap[cur].clear();
for(int k=; k<hashmap[cur^].size; k++)
{
LL s=hashmap[cur^].status[k];
LL v=hashmap[cur^].value[k];
ULL p=hashmap[cur^].path[k];
decode(s);
p<<=;
if( g[i][j]== )
{
trycover(,v,p+,i,j);
trycover(,v,p+,i,j);
}
else trycover(g[i][j],v,p+g[i][j],i,j);
}
}
}
} void print() //答案及路径输出。
{
int ans=;ULL mapp=;
for(int i=; i<hashmap[cur].size; i++)
{
int big=;
decode(hashmap[cur].status[i]);
for(int y=; y<=m; y++) big=max(big, code[y]); //求最大编号
if(big<=)
{
mapp=hashmap[cur].path[i];
ans+=hashmap[cur].value[i];
}
}
printf("%d\n", ans);
if(ans)
{
memset(str,'\0',sizeof(str));
for(int i=n-; i>=; i--)
{
for(int j=m-; j>=; j--)
{
if(mapp&) str[i][j]='#';
else str[i][j]='o';
mapp>>=;
}
}
for(int i=; i<n; i++) printf("%s\n",str[i]);
}
} int main()
{
freopen("input.txt", "r", stdin);
int t;cin>>t;
while(t--)
{
memset(g,,sizeof(g));
scanf("%d%d", &n,&m);
for(int i=; i<n; i++)
{
for(int j=; j<m; j++)
{
char c=getchar();
if(c=='#') g[i][j]=; //黑色
else if(c=='o') g[i][j]=;
else if(c=='.') g[i][j]=;
else j--;
}
}
cal();print();printf("\n");
}
return ;
}

AC代码

UVA 10572 Black & White (状压DP)的更多相关文章

  1. UVa 1204 Fun Game (状压DP)

    题意:有一些小孩(至少两个)围成一圈,有 n 轮游戏,每一轮从某个小孩开始往左或者往右伟手帕,拿到手帕写上自己的性别(B,G),然后以后相同方向给下一个. 然后在某个小孩结束,给出 n 轮手帕上的序列 ...

  2. UVa 11825 Hackers' Crackdown (状压DP)

    题意:给定 n 个计算机的一个关系图,你可以停止每台计算机的一项服务,并且和该计算机相邻的计算机也会终止,问你最多能终止多少服务. 析:这个题意思就是说把 n 台计算机尽可能多的分成一些组,使得每组的 ...

  3. UVa 1252 Twenty Questions (状压DP+记忆化搜索)

    题意:有n件物品,每件物品有m个特征,可以对特征进行询问,询问的结果是得知某个物体是否含有该特征,要把所有的物品区分出来(n个物品的特征都互不相同), 最小需要多少次询问? 析:我们假设心中想的那个物 ...

  4. UVA - 1252 Twenty Questions (状压dp+vis数组加速)

    有n个物品,每个物品有m个特征.随机选择一个物品让你去猜,你每次可以询问一个特征的答案,问在采取最优策略时,最坏情况下需要猜的次数是多少. 设siz[S]为满足特征性质集合S的特征的物品总数,dp[S ...

  5. UVA 11825 Hackers’ Crackdown 状压DP枚举子集势

    Hackers’ Crackdown Miracle Corporations has a number of system services running in a distributed com ...

  6. UVA 1412 Fund Management (预处理+状压dp)

    状压dp,每个状态可以表示为一个n元组,且上限为8,可以用一个九进制来表示状态.但是这样做用数组开不下,用map离散会T. 而实际上很多九进制数很多都是用不上的.因此类似uva 1601 Mornin ...

  7. 状压DP UVA 10817 Headmaster's Headache

    题目传送门 /* 题意:学校有在任的老师和应聘的老师,选择一些应聘老师,使得每门科目至少两个老师教,问最少花费多少 状压DP:一看到数据那么小,肯定是状压了.这个状态不好想,dp[s1][s2]表示s ...

  8. UVa 11825 (状压DP) Hackers' Crackdown

    这是我做状压DP的第一道题,状压里面都是用位运算来完成的,只要耐下心来弄明白每次位运算的含义,还是容易理解的. 题意: 有编号为0~n-1的n台服务器,每台都运行着n中服务,每台服务器还和若干台其他服 ...

  9. UVA - 1252 Twenty Questions (状压dp)

    状压dp,用s表示已经询问过的特征,a表示W具有的特征. 当满足条件的物体只有一个的时候就不用再猜测了.对于满足条件的物体个数可以预处理出来 转移的时候应该枚举询问的k,因为实际上要猜的物品是不确定的 ...

  10. 状压dp的题目列表 (一)

    状压dp的典型的例子就是其中某个数值较小. 但是某个数值较小也不一定是状压dp,需要另外区分的一种题目就是用暴力解决的题目,例如UVA818 紫书215 题目列表: ①校长的烦恼 UVA10817 紫 ...

随机推荐

  1. 页面跳转问题,多次 push 到新的页面的问题的解决方法

    今日在做一个扫一扫的功能,突然发现多次点击了扫一扫的图片后,造成多次触发轻拍手势,就多次push到新的页面,本想在轻拍手势内对push的进行拦截,但是又觉得如果有好多的地方都要实现对该问题的解决岂不是 ...

  2. 我所理解的Restful API最佳实践

    一直在公司负责API数据接口的开发,期间也遇到了不小的坑,本篇博客算是做一个小小的记录. 1. 不要纠结于无意义的规范    在开始本文之前,我想先说这么一句:RESTful 真的很好,但它只是一种软 ...

  3. C#总结---方法的out参数和ref参数

    我们知道,在c#中,当我们在一个方法中想要访问另一个方法中的变量的时候,有两种解决方案---参数和返回值.但当需要返回多个值,并且是不同类型的值的之后应该怎么办呢?解决方案可以是 (1)将所有类型数据 ...

  4. JavaScript代码风格和分号使用问题

    1.推荐代码风格 JavaScript Standard Style  规定相对松散更多人使用此规范 Airbnb JavaScript Style  规定更严格但也没毛病 2.JavaScript代 ...

  5. 多行文字的垂直居中或高度不同的图片垂直居中---:after伪类+content

    如何让多行文字垂直居中?或者如何让图片垂直居中? 1.demo--css .box { width: 300px; height: 300px; background-color: #f5e79e; ...

  6. 萌新学习图的强连通(Tarjan算法)笔记

    --主要摘自北京大学暑期课<ACM/ICPC竞赛训练> 在有向图G中,如果任意两个不同顶点相互可达,则称该有向图是强连通的: 有向图G的极大强连通子图称为G的强连通分支: Tarjan算法 ...

  7. CodeForces691C 【模拟】

    这一题的模拟只要注意前后导零就好了... 感受就是... 如果是比赛中模拟题打好..要盯着注意点,测试不同的情况下的注意点..起码要针对性测试10分钟.. 还是蛮简单的,但是自己打烦了,应该,队友代码 ...

  8. unity5之代码创建状态机,玩的666

    http://blog.csdn.net/litaog00/article/details/50483189 最近做项目的时候用到了状态机,网上搜了一下帖子,大部分都是简单介绍使用方法的,讲解的详细的 ...

  9. UIWebView 使用

    UIWebView是iOS sdk中一个最常用的控件.是内置的浏览器控件,我们可以用它来浏览网页.打开文档等等.这篇文章我将使用这个控件,做一个简易的浏览器.如下图: 我们创建一个Window-bas ...

  10. E. XOR and Favorite Number (莫队板子题)

    题目链接 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int read() { , ...