题意:有一个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. Linux : Linux命令

    接触linux一段时间了,在网上搜罗了一些命令,以备后用.我有点爱上Linux了. 来自 :http://www.php100.com/html/webkaifa/Linux/2009/1106/34 ...

  2. const_cast标准转换运算符

    #include <iostream> using namespace std; class A { public: A() { a=; } public: int a; }; void ...

  3. 网络编程-http连接-GET&POST

    GetRequest package com.net.http; import java.io.BufferedReader; import java.io.IOException; import j ...

  4. 看后端程序员调试CORS的姿势

    # 目录 为什么有同源策略? 需要解决的问题 CORS跨域请求方案 preflight withCredentials 附:高效.优雅地调试CORS实现 为什么有同源策略?       同源策略Sam ...

  5. E20190402-hm

    porxy n. 代理服务器; 代表权; 代理人,代替物; 委托书; enroll  v. 招收; 注册; 登记; 加入; enrollment n. 注册; 登记; 入会;

  6. Chrome开发者工具 debug 调试

    Chrome 的开发者工具分为 8 个大模块,每个模块及其主要功能为: Element 标签页: 用于查看和编辑当前页面中的 HTML 和 CSS 元素. Network 标签页:用于查看 HTTP ...

  7. tinyxml一些应注意的问题

     今天在对使用tinyxml库的程序调试的时候,出现的一些问题让人很纠结,特记以此... 在对TixmlDocument创建时我是用new创建的,然后在用完之后我用delete释放掉,可是用gdb调试 ...

  8. sequence(2018.10.23)

    建出差分序列,可以发现最早出现的回文串就是答案,自己想想就懂了. \(O(N)\)找出回文串就好了,字符串\(hash\)或者\(manacher\)都能在合法时间内得到答案. #include< ...

  9. Image.resize()和Image.thumbnail()的区别

    Image.resize()和Image.thumbnail()的区别 根据代码和代码注释, 这两个函数都是对图片进行缩放, 两者的主要区别如下: resize()函数会返回一个Image对象, th ...

  10. 牛客练习赛42A(字符串)

    传送门 结论是:一定是选取最长的那个AB连续子串. 把题面要求的a*b + a + b转化一下成(a + 1)*(b + 1) - 1,即可发现如果选取前缀后缀不连续的两段作为答案,则显然有更优解,即 ...