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[18]; bool d[18][18],h[18]; int n,ans=1e9,color,in[18],col[18],incol[22],rec[65538]; //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[18],tmp2[22],tmp3[18],cnt,p; for(int i=1;i<=n;++i) tmp1[i]=h[i],tmp3[i]=in[i]; for(int i=1;i<=color;++i) tmp2[i]=incol[i]; //用tmp数组保存当前状态 for(int i=1;i<=color;++i) { cnt=0; p=zip; if(!incol[i]) continue; while(incol[i]) //一直使用该种颜色直到无法再刷 { for(int j=1;j<=n;++j) if(h[j]&&col[j]==i){ --incol[i]; h[j]=0; ++cnt; p+=1<<j; //取出有标记的点 for(int u=1;u<=n;++u) //枚举下一层 if(d[j][u]){ --in[u]; if(in[u]==0) h[u]=1,++incol[col[u]]; //打上标记 } } } dfs(t+1,x+cnt,p); for(int j=1;j<=n;++j) h[j]=tmp1[j],in[j]=tmp3[j]; for(int j=1;j<=color;++j) incol[j]=tmp2[j]; //当前状态回溯 } } int main(){ scanf("%d",&n); for(int i=1;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=1;i<=n;++i) for(int j=1;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]=1,++in[j]; //建图 for(int i=1;i<=n;++i) if(!in[i]) h[i]=1,++incol[col[i]]; dfs(0,0,0); printf("%d",ans); return 0; }