题意:有一个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. 【224】◀▶ IDL NetCDF 文件操作说明

    参考:I/O - NetCDF Routines —— NetCDF 操作函数 01   NCDF_OPEN 打开一个 NetCDF 文件. 02   NCDF_CLOSE 关闭一个 NetCDF 文 ...

  2. UVaLive 6847 Zeroes (找规律,水题)

    题意 :给定一个范围,然后让你求在这个范围内所有的数的阶乘末尾0的个数有多少种. 析:找规律,写几个就会发现每隔5个会增加一个0,因为要么乘10了,要么乘5了. 代码如下: #pragma comme ...

  3. BlocksKit的使用

    一.引言 众所周知Block已被广泛用于iOS编程.它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调.Block比传统回调函数有2点优势: 允许在调用点上下文书写执行逻辑,不用分离函数 ...

  4. 使用you-get下载网页小视频(实际上你可以下载任意你想要的web网页中的内容)

    1. 什么是you-get? You-Get是一个小型的命令行实用程序,用于从Web下载媒体内容(视频,音频,图像),如果没有其他方便的方法可以尝试使用you-get. 2.安装you-get 打开命 ...

  5. 719. Find K-th Smallest Pair Distance

    Given an integer array, return the k-th smallest distance among all the pairs. The distance of a pai ...

  6. [Xcode 实际操作]八、网络与多线程-(14)使用网址会话对象URLSession将地理坐标转换为地名

    目录:[Swift]Xcode实际操作 本文将演示如果通过网址会话对象,将地理坐标转换为地名. 网址会话对象URLSession具有在后台上传和下载.暂停和恢复网络操作.丰富的代理模式等优点. 在项目 ...

  7. TCP协议怎么关闭?

    TCP协议是一个面向连接的传输层协议,那如果避免遭到破坏,该怎样正确关闭呢?一般正常关闭TCP连接是采用四次挥手机制,其实主体就两个,客户端和服务器交互传递,且连续四次,传递的东西是FIN数据包和AC ...

  8. C 语言实例 - 计算 int, float, double 和 char 字节大小

    C 语言实例 - 计算 int, float, double 和 char 字节大小 C 语言实例 C 语言实例 使用 sizeof 操作符计算int, float, double 和 char四种变 ...

  9. JIRA中的标记语言的语法参考

    前言 看到网上有的文章说JIRA是使用Textile这门标记语言,有些语法和Wikitext和Markdown相像.JIRA在2017年进行了一次大更新,某些语法可能和以前不大一样,这里纪录一下常用的 ...

  10. 获取元素属性 和 获取元素的CSS属性