UVA 10572 Black & White (状压DP)
题意:有一个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)的更多相关文章
- UVa 1204 Fun Game (状压DP)
题意:有一些小孩(至少两个)围成一圈,有 n 轮游戏,每一轮从某个小孩开始往左或者往右伟手帕,拿到手帕写上自己的性别(B,G),然后以后相同方向给下一个. 然后在某个小孩结束,给出 n 轮手帕上的序列 ...
- UVa 11825 Hackers' Crackdown (状压DP)
题意:给定 n 个计算机的一个关系图,你可以停止每台计算机的一项服务,并且和该计算机相邻的计算机也会终止,问你最多能终止多少服务. 析:这个题意思就是说把 n 台计算机尽可能多的分成一些组,使得每组的 ...
- UVa 1252 Twenty Questions (状压DP+记忆化搜索)
题意:有n件物品,每件物品有m个特征,可以对特征进行询问,询问的结果是得知某个物体是否含有该特征,要把所有的物品区分出来(n个物品的特征都互不相同), 最小需要多少次询问? 析:我们假设心中想的那个物 ...
- UVA - 1252 Twenty Questions (状压dp+vis数组加速)
有n个物品,每个物品有m个特征.随机选择一个物品让你去猜,你每次可以询问一个特征的答案,问在采取最优策略时,最坏情况下需要猜的次数是多少. 设siz[S]为满足特征性质集合S的特征的物品总数,dp[S ...
- UVA 11825 Hackers’ Crackdown 状压DP枚举子集势
Hackers’ Crackdown Miracle Corporations has a number of system services running in a distributed com ...
- UVA 1412 Fund Management (预处理+状压dp)
状压dp,每个状态可以表示为一个n元组,且上限为8,可以用一个九进制来表示状态.但是这样做用数组开不下,用map离散会T. 而实际上很多九进制数很多都是用不上的.因此类似uva 1601 Mornin ...
- 状压DP UVA 10817 Headmaster's Headache
题目传送门 /* 题意:学校有在任的老师和应聘的老师,选择一些应聘老师,使得每门科目至少两个老师教,问最少花费多少 状压DP:一看到数据那么小,肯定是状压了.这个状态不好想,dp[s1][s2]表示s ...
- UVa 11825 (状压DP) Hackers' Crackdown
这是我做状压DP的第一道题,状压里面都是用位运算来完成的,只要耐下心来弄明白每次位运算的含义,还是容易理解的. 题意: 有编号为0~n-1的n台服务器,每台都运行着n中服务,每台服务器还和若干台其他服 ...
- UVA - 1252 Twenty Questions (状压dp)
状压dp,用s表示已经询问过的特征,a表示W具有的特征. 当满足条件的物体只有一个的时候就不用再猜测了.对于满足条件的物体个数可以预处理出来 转移的时候应该枚举询问的k,因为实际上要猜的物品是不确定的 ...
- 状压dp的题目列表 (一)
状压dp的典型的例子就是其中某个数值较小. 但是某个数值较小也不一定是状压dp,需要另外区分的一种题目就是用暴力解决的题目,例如UVA818 紫书215 题目列表: ①校长的烦恼 UVA10817 紫 ...
随机推荐
- atof,atoi,atol,strtod,strtol,strtoul
字符串处理函数 atof 将字串转换成浮点型数 atoi 字符串转换成整型数 atol 函数名: atol 功 能: 把字符串转换成长整型数 用 法: long atol(const char *np ...
- CodeForces 349B Color the Fence (DP)
题意:给出1~9数字对应的费用以及一定的费用,让你输出所选的数字所能组合出的最大的数值. 析:DP,和01背包差不多的,dp[i] 表示费用最大为 i 时,最多多少位,然后再用两个数组,一个记录路径, ...
- 一款基于Bootstrap的js分页插件bootstrap-paginator使用实例
Bootstrap Paginator是一款基于Bootstrap的js分页插件,功能很丰富.它提供了一系列的参数用来支持用户的定制,提供了公共的方法可随时获得插件状态的改变,以及事件来监听用户的动作 ...
- MFC中CArray类原理及其应用
1.CArray类应用 函数简介CArray::GetSize int GetSize( ) const;取得当前数组元素个数. CArray::GetUpperBound int GetUpperB ...
- Codeforces 67A【模拟】
题意: 给一个字符串代表相邻学生的比较,L代表左边多,R表示右边多,=表示左右相等. 保证每个人拿糖>=1,在分糖最少的情况下,输出每个学生所分得的糖. 思路: 模拟一下,第一个人一开始拿1个, ...
- Rigging a Versatile Weapon Bone for 3ds Max
说明:先添加weapon到点的约束,位置,方向约束都调整好了后再建立点到手,hip的父子关系,注意这个顺序 加点的方法 点设置成box的方法: http://hewiki.heroengine.com ...
- 洛谷P3338 [ZJOI2014]力(FFT)
传送门 题目要求$$E_i=\frac{F_i}{q_i}=\sum_{j=1}^{i-1}\frac{q_j}{(i-j)^2}-\sum_{j=i+1}^n\frac{q_j}{(j-i)^2}$ ...
- 类的property特性
目录 什么是 property特性 简单示例 property属性的两种方式 装饰器 类属性方式 property+类的封装 应用 私有属性添加getter和setter方法 使用property升级 ...
- 指向函数的指针和block
原文网址: http://www.cnblogs.com/cxbblog/p/3841226.html 一:block基础知识 block基础知识 基本概念:block是用来保存一段代码的:^:是bl ...
- perl C/C++ 扩展(一)
通过h2xs 中间件,我们可以快速的使用c或则C++ 库来实现perl 扩展功能 第一讲:跑通hello world 程序******************************我们使用命令:h2 ...