P1283 平板涂色

时间:2021-10-24 15:16:42

P1283 平板涂色

dfs 记忆化搜索

将矩阵转化为图求解,然后我们发现这是个DAG,于是就可以愉快地跑搜索了。

进行dfs时,我们可以用类似拓扑排序的方法。每次将上面所有矩形都被刷过(入度in[ i ]==0)的满足条件的矩形用h数组打个标记

用incol数组表示目前h数组中有几种颜色,然后枚举可转移状态进行dfs

发现数据n<16,于是我们可以将状态用二进制存起来(0/1表示是否已被刷)进行记忆化搜索

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline int _max(int &a,int &b) {return a>b ? a:b;}
inline int _min(int &a,int &b) {return a<b ? a:b;}
struct matr{int x1,y1,x2,y2;}a[];
bool d[][],h[];
int n,ans=1e9,color,in[],col[],incol[],rec[]; //rec:存状态,用作记忆化
inline void dfs(int t,int x,int zip){ //t:拿起刷子最少次数 x:已刷几个格子 zip:当前状态
if(rec[zip]&&rec[zip]<=t) return ; //记忆化搜索
else rec[zip]=t;
if(x==n) {ans=_min(ans,t); return ;} //所有矩形已刷过
int tmp1[],tmp2[],tmp3[],cnt,p;
for(int i=;i<=n;++i) tmp1[i]=h[i],tmp3[i]=in[i];
for(int i=;i<=color;++i) tmp2[i]=incol[i]; //用tmp数组保存当前状态
for(int i=;i<=color;++i)
{
cnt=; p=zip;
if(!incol[i]) continue;
while(incol[i]) //一直使用该种颜色直到无法再刷
{
for(int j=;j<=n;++j)
if(h[j]&&col[j]==i){
--incol[i]; h[j]=; ++cnt; p+=<<j; //取出有标记的点
for(int u=;u<=n;++u) //枚举下一层
if(d[j][u]){
--in[u];
if(in[u]==) h[u]=,++incol[col[u]]; //打上标记
}
}
}
dfs(t+,x+cnt,p);
for(int j=;j<=n;++j) h[j]=tmp1[j],in[j]=tmp3[j];
for(int j=;j<=color;++j) incol[j]=tmp2[j]; //当前状态回溯
}
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;++i){
scanf("%d%d%d%d%d",&a[i].y1,&a[i].x1,&a[i].y2,&a[i].x2,&col[i]);
color=_max(color,col[i]);
}
for(int i=;i<=n;++i)
for(int j=;j<=n;++j)
if(i!=j&&a[i].y2==a[j].y1&&a[i].x1<a[j].x2&&a[i].x2>a[j].x1)
d[i][j]=,++in[j]; //建图
for(int i=;i<=n;++i) if(!in[i]) h[i]=,++incol[col[i]];
dfs(,,);
printf("%d",ans);
return ;
}