其他分享
首页 > 其他分享> > UVA10572 Black & White 题解

UVA10572 Black & White 题解

作者:互联网

插头 DP

Statement

UVA10572 Black & White - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Solution

因为相邻格子连通性取决于颜色而不取决于几联通,所以我们需要记录轮廓线上方 \(m\) 个格子的颜色和连通性

注意区分颜色和连通性的区别,颜色相同不一定联通

同时注意到题面的特殊限制,所以需要保存 \((i-1,j-1)\) 的颜色

连通性需要使用广义括号表示法/最小表示法表示,这里采用最小表示法

联通状态最多有 8 种不同的,直接上 8 进制即可。

即,设 \(f(i,j,sta,col,cp)=val\) 表示正在涂色 \((i,j)\) ,轮廓线连通性 $sta $ ,颜色 \(col\) ,\((i-1,j-1)\) 颜色 \(cp\) 的方案数

考虑转移,设 \(u,l,lu\) 分别表示 \((i,j)\) 颜色是否和 上面/左边/左上 格子颜色相同

容易 \(O(1)\) 推知下一次的 \(col^{\prime},cp^{\prime}\) ,关键在于连通性,解码 \(sta\to a\)

重新编码的时候注意要满足最小表示法。

显然,如果 \(u\wedge l\wedge lu\) ,那就直接 G 了

考虑什么时候也会 GG:

第二条:可以考虑试填法,\((i,j-1)\) 位必然和 \((i,j)\) 相同。因为如若不相同,那么 \((i-1,j-1)\) 必然与 \((i,j)\) 相同,卡死了。同时 \((i-1,j+1)\) 位也和 \((i,j)\) 相同。因为其他格子和 \((i-1,j)\) 连通性都不同,容易发现 \((i-1,j)\) 被孤立,GG

第三条:很显然,此时轮廓线下方格子应该全部都涂黑

输出方案的话在 DP 过程中记录一个 pre 就可以了

Code

小记:写了一个晚上,c。下午 4:30 左右开始思考(穿插看 ppt 和题解),5:30 大致理解。吃完饭 6:30 开始按照自己的理解写,写到 \(9:00\) ,死活不对,开始看题解代码

漏掉的细节:

#include<bits/stdc++.h>
#define bit(x) (1<<(x))
using namespace std;
typedef long long ll;
const int mod = 233333;
const int M = mod + 5;
const int N = 20;

int read(){
	int s=0,w=1; char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
	return s*w;
}

int a[N],num[N],pre[100][mod+5];
char mp[N][N],op[100][mod+5];
int T,n,m,now;

struct Hash_map{
	int nex[M],head[M];
	int sta[M],col[M],val[M];//二进制表示 col ,八进制表示 sta
	int siz;
	
	void clear(){
		memset(head,0,sizeof(head));
		siz=0;
	}
	void insert(int s,int c,int v,int id,int fa,char o){
		for(int i=head[s%mod];i;i=nex[i])
			if(sta[i]==s&&col[i]==c)
				return val[i]+=v,void();
		sta[++siz]=s,col[siz]=c,val[siz]=v;
		pre[id][siz]=fa,op[id][siz]=o;
		nex[siz]=head[s%mod],head[s%mod]=siz; 
	}
}dp[2];

void decode(int s){//解码
	for(int i=m-1;~i;--i)
		a[i]=s&7,s>>=3;
}
int encode(){//最小表示法
	memset(num,-1,sizeof(num));
	int k=-1,res=0;
	for(int i=0;i<m;++i){
		if(num[a[i]]==-1)num[a[i]]=++k;
		res=(res<<3)|num[a[i]];
	}
	return res;
}
void change(int x,int y){//刷颜色
	for(int i=0;i<m;++i)
		if(a[i]==x)a[i]=y;
}
void DP(int i,int j,int c){
	for(int k=1;k<=dp[now].siz;++k){
		int cc=dp[now].col[k];
		int u=i?(cc>>j&1)==c:0;
		int l=j?(cc>>(j-1)&1)==c:0;//边界直接认为不相同
		int lu=(cc>>m)==c;// 二进制第 m 位存放 (i-1,j-1)
		if(u&&l&&lu)continue;
		if(i==n-1&&j==m-1&&!u&&!l&&lu)continue;
		decode(dp[now].sta[k]);
		if(i&&!u){
			int s1=0,s2=0;
			for(int t=0;t<m;++t)
				s1+=a[t]==a[j],s2+=((cc>>t&1)!=c);
            //s1:连通性相同格子个数, s2: 与 u 颜色相同个数
			if(s1==1){
				if(s2>1)continue;
				if(i<n-1||j<m-2)continue;
			}
		}
		
		if(u&&l){
			if(a[j]!=a[j-1])
				change(a[j],a[j-1]);
		}else if(!u&&l)a[j]=a[j-1];
		else if(!u&&!l)a[j]=m;
		if(cc&(1<<j))cc|=1<<m; else cc&=~(1<<m);
		if(c)cc|=1<<j; else cc&=~(1<<j);
		
		dp[now^1].insert(encode(),cc,dp[now].val[k],i*m+j,k,c?'#':'o');
	}
}
void print(int k){
	for(int i=n-1;i>=0;--i)
		for(int j=m-1;j>=0;--j)
			mp[i][j]=op[i*m+j][k],
			k=pre[i*m+j][k];
	for(int i=0;i<n;++i)puts(mp[i]);
}

signed main(){
	T=read();
	while(T--){
		n=read(),m=read();
		memset(mp,0,sizeof(mp));
		for(int i=0;i<n;++i)
			for(int j=0;j<m;++j)
				scanf(" %c",&mp[i][j]);
		now=0,dp[0].clear(),dp[0].insert(0,0,1,0,0,0);
		for(int i=0;i<n;++i)
			for(int j=0;j<m;++j){
				dp[now^1].clear();
				if(mp[i][j]!='#')DP(i,j,0);
				if(mp[i][j]!='o')DP(i,j,1);
				now^=1;
			}
		int ans=0,k;
		for(int i=1;i<=dp[now].siz;++i){
			int mx=0;
			decode(dp[now].sta[i]);
			for(int j=0;j<m;++j)
				mx=max(mx,a[j]);
			if(mx>1)continue;
			ans+=dp[now].val[i];
			k=i;
		}
		printf("%d\n",ans);
		if(ans)print(k);
		puts("");//UVA 神奇要求
	}	
	return 0;
}

标签:wedge,颜色,连通性,格子,int,题解,&&,UVA10572,White
来源: https://www.cnblogs.com/wyb-sen/p/16217646.html