luogu P1283 【平板涂色】
闲话
第一道完全靠自己想出的蓝题,怎么说也要写篇题解纪念一下
思路
看完题面,使用DFS应该是很显然了
但是不同于普通深搜的枚举,这道题有一个限制条件
那就是一个矩形只能在所有紧靠它上方的矩形涂色后,才能涂色
所以我在开始搜索前做了预处理,处理出了每个矩形在涂色前有哪些矩形需要先涂好,搜索时再去判断
每个需要预涂的矩形是否已先涂好
另外,由于搜索时我是一次性将所有可以涂色的涂完,所以需要先对所有矩形按照从上到下的顺序进行排序,这样就可以保证在一次性的涂色中,当我要涂下方的矩形时,上方的同颜色矩形已经涂完了
之后的事情就非常简单,只要照着DFS的模板打就行了
没有剪枝好像由于数据范围过小水过了
AC代码如下(带很详细的注释)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,minn=1e9,tot,sum[17],ss[17][17],ans[1000],v;sum[i]代表第i个矩形上方紧靠着的矩形数
struct node{ //ss[i][j]代表第i个矩形的第j个紧靠着的矩形是哪一个
int x1,y1,x2,y2;
int d;
}s[17];//s[i].x1和y1代表第i个矩形左上方点的坐标,s[i].x2和y2代表右下方点的坐标,s[i].d代表要涂的颜色
bool ex[17];
void dfs(int k,int su)//k为拿起刷子的次数,su为已经涂色的矩形数目
{
if(su==n)
{
minn=min(minn,k);
return;
}
for(int g=1;g<=tot;g++)//tot为颜色总数
{
int f=su,lin[17],ff=0;//lin[i]为临时数组,用来记录新涂了哪些矩形,方便之后回溯
for(int i=1;i<=16;i++)lin[i]=0;
for(int i=1;i<=n;i++)
{
if(s[i].d==g&&ex[i]==0)
{
int si=0;
for(int j=1;j<=sum[i];j++)//判断需要的矩形有几个已经预先涂好
if(ex[ss[i][j]])si++;
if(si==sum[i])//如果都涂好了就说明可以涂这个矩形了
{
ex[i]=1;lin[++ff]=i;//标记为已涂过
su++;
}
}
}
if(f==su)continue;//如果用这个颜色不能涂更多矩形,就换一个颜色
ans[++v]=g;//记录每一次使用的颜色
if(ans[v-1]!=ans[v]&&v!=1)//如果与上一次使用的颜色不同,就将次数加1
dfs(k+1,su);
if(ans[v-1]==ans[v]||v==1)//如果与上一次使用的颜色相同或者是第一次使用,就不加次数
dfs(k,su);
ans[v]=0;--v;//回溯
for(int i=1;i<=ff;i++)
ex[lin[i]]=0,su--;
}
}
bool cmp(node a,node b)
{
if(a.x1==b.x1)return a.y1<b.y1;
return a.x1<b.x1;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i].x1>>s[i].y1>>s[i].x2>>s[i].y2>>s[i].d;
tot=max(tot,s[i].d);//更新tot
}
sort(s+1,s+n+1,cmp);//从上到下排序
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i!=j)
{ //如果其他矩形的y1与y2有一个在此矩形的y1与y2之间,并且是紧靠着的,就是合法的
if(((s[j].y1>=s[i].y1&&s[j].y1<s[i].y2)||(s[j].y2>s[i].y1&&s[j].y2<=s[i].y2))&&s[j].x1<s[i].x1&&s[i].x1==s[j].x2)
{
sum[i]++;//个数加1
ss[i][sum[i]]=j;//记录是哪个矩形
}
}
}
dfs(1,0);
printf("%d",minn);
return 0;
}