平板涂色
题目描述
CE 数码公司开发了一种名为自动涂色机(APM)的产品。它能用预定的颜色给一块由不同尺寸且互不覆盖的矩形构成的平板涂色。
为了涂色,APM 需要使用一组刷子。每个刷子涂一种不同的颜色 C i C_i Ci 。APM 拿起一把有颜色 C i C_i Ci 的刷子,并给所有颜色为 C i C_i Ci 且符合下面限制的矩形涂色:
为了避免颜料渗漏使颜色混合,一个矩形只能在所有紧靠它上方的矩形涂色后,才能涂色。例如图中矩形 F F F 必须在 C C C 和 D D D 涂色后才能涂色。注意,每一个矩形必须立刻涂满,不能只涂一部分。
写一个程序求一个使 APM 拿起刷子次数最少的涂色方案。注意,如果一把刷子被拿起超过一次,则每一次都必须记入总数中。
输入格式
第一行为矩形的个数 N N N。下面有 N N N 行描述了 N N N 个矩形。每个矩形有 5 5 5 个整数描述,左上角的 y y y 坐标和 x x x 坐标,右下角的 y y y 坐标和 x x x 坐标,以及预定颜色。
平板的左上角坐标总是 ( 0 , 0 ) (0,0) (0,0)。
输出格式
一个整数,表示拿起刷子的最少次数。
样例 #1
样例输入 #1
7
0 0 2 2 1
0 2 1 6 2
2 0 4 2 1
1 2 4 4 2
1 4 3 6 1
4 0 6 4 1
3 4 6 6 2
样例输出 #1
3
提示
1 ≤ C i ≤ 20 1\le C_i \le 20 1≤Ci≤20, 0 ≤ x i , y i ≤ 99 0 \le x_i,y_i \le 99 0≤xi,yi≤99, 1 ≤ N ≤ 16 1\le N \le 16 1≤N≤16。
本人做题历程及吐槽
第一阶段,认为只要按每个矩形右下Ry从小到大排序后用近似贪心的dfs做
#include<bits/stdc++.h>
using namespace std;
int n,ans=0x7f7f7f7f;
struct node{
int lx,ly,rx,ry,colour;
}a[999];
int s[109][109];
bool flag[999];
bool cmp(node x,node y){
if(x.ry==y.ry)
return x.ry<y.ry;
else return x.lx<x.rx;
}
void dfs(int cnt,int sum,int last){
if(cnt>n||last>n)return;
if(cnt==n){
ans=min(sum,ans);
cout<<ans;
exit(0);
//cout<<"find!!!!!!!!!!!!!!!!!!!!==="<<ans<<endl;
}
//cout<<cnt<<" "<<sum<<" "<<last<<endl;
for(int i=cnt;i<=n;i++){
if(flag[i]==0){
flag[i]=1;
if(a[i].colour!=a[last].colour){
if(cnt==i)continue;
dfs(i,sum+1,cnt);
flag[i]=0;
}
else{
if(cnt==i)continue;
dfs(i,sum,cnt);
flag[i]=0;
}
}
}
}
int main(){
cin>>n;
memset(s,-0x3f,sizeof(s));
for(int i=1;i<=n;i++){
cin>>a[i].lx>>a[i].ly>>a[i].rx>>a[i].ry>>a[i].colour;
// for(int x=a[i].lx;x<=a[i].rx;x++){
// for(int y=a[i].ly;y<=a[i].ry;y++){
// s[x][y]=a[i].colour;
// }
// }
}
sort(a+1,a+1+n,cmp);
dfs(1,0,1);
cout<<ans;
return 0;
}
…
第二阶段
暴力yyds
#include<bits/stdc++.h>
using namespace std;
int n,ans=0x7f7f7f,sum=0;
struct node{
int lx,ly,rx,ry,colour;
}a[999];
int s[109][109],k[109];
bool flag;
bool cmp(node x,node y){
// if(x.ry==y.ry)
return x.ry<y.ry;
// else return x.lx<x.rx;
}
int main(){
cin>>n;
memset(s,-1,sizeof(s));
for(int i=1;i<=n;i++){
cin>>a[i].lx>>a[i].ly>>a[i].rx>>a[i].ry>>a[i].colour;
}
sort(a+1,a+1+n,cmp);
//dfs(1,a[1].colour,1,0);
for(int i=1;i<=n;i++){
flag=0;
for(int j=a[i].lx;j<=a[i].rx;j++){
if(s[a[i].ly][j]==a[i].colour||s[a[i].ry][j]==a[i].colour){
flag=1;break;
}
}
for(int j=a[i].ly;j<=a[i].ry;j++){
if(s[j][a[i].lx]==a[i].colour||s[j][a[i].rx]==a[i].colour){
flag=1;break;
}
}
if(!flag)
sum++;
// for(int j=a[i].lx;j<=a[i].rx;j++){
// s[a[i].ly][j]=s[a[i].ry][j]=a[i].colour;
// }
// for(int j=a[i].ly;j<=a[i].ry;j++){
// s[j][a[i].lx]=s[j][a[i].rx]=a[i].colour;
// }
for(int i=a[i].lx;i<=a[i].rx;i++){
for(int j=a[i].ly;j<=a[i].ry;j++){
s[i][j]=a[i].colour;
}
}
}
// for(int i=0;i<=n;i++){
// for(int j=0;j<=n;j++){
// cout<<s[i][j]<<" ";
// }
// cout<<endl;
// }
cout<<sum;
return 0;
}
nsdd,ds,bl是口口口口口口口。
真正的思路
- 把暴力用到的图与dfs结合,加之判断,
慢火小熬1坤小时即可(其实就是改了很长时间啦) - 我们只需要把每个矩形在图上进行标记,在判断当前矩形是否合法时判断它上方LX~RX是否全部被标记(即全部被涂色过)
void add(int x)//涂色后为0
{
for(int j=a[x].lx+1;j<=a[x].rx;j++)
for(int k=a[x].ly+1;k<=a[x].ry;k++)
mapp[j][k]=0;
}
void del(int x)//清除涂色
{
for(int j=a[x].lx+1;j<=a[x].rx;j++)
for(int k=a[x].ly+1;k<=a[x].ry;k++)
mapp[j][k]=a[x].col;
}
int pd(int x)//判断上面是否涂色
{
for(int i=a[x].ly+1;i<=a[x].ry;i++)
if(mapp[a[x].lx][i]!=0) return 0;//如果上面有一格没涂就不能涂这格
return 1;
}
dfs部分,我们从(0,0)开始搜索,每一个矩形有两种可能,一种是与之前矩形颜色相同,另一种自然是不同,根据结果判断sum(需要的次数)是否++
for(int i=1;i<=n;i++)//枚举矩形
{
if(!a[i].vis&&pd(i))//如果这个矩形没有被涂色 且可以涂
{
if(a[i].col==a[now].col&&pd(i))//当颜色一样时
{
a[i].vis=1;
add(i);
dfs(i,sum,cnt+1);//拿笔数不用加1 而已涂矩形要加1
a[i].vis=0;
del(i);
}
if(a[i].col!=a[now].col&&pd(i))//当颜色不一样时
{
a[i].vis=1;
add(i);
dfs(i,sum+1,cnt+1);//都要加1
a[i].vis=0;
del(i);
}
}
}
归结以上就可以得出ac code啦
#include<bits/stdc++.h>
using namespace std;
const int N=114;
int n,num,ans=1e9+7;
int mapp[N][N];//存图
struct node{
int lx,rx,ly,ry,col,vis;//判断是否涂色
}a[21];
void add(int x)//涂色后为0
{
for(int j=a[x].lx+1;j<=a[x].rx;j++)
for(int k=a[x].ly+1;k<=a[x].ry;k++)
mapp[j][k]=0;
}
void del(int x)//清除涂色
{
for(int j=a[x].lx+1;j<=a[x].rx;j++)
for(int k=a[x].ly+1;k<=a[x].ry;k++)
mapp[j][k]=a[x].col;
}
int pd(int x)//判断上面是否涂色
{
for(int i=a[x].ly+1;i<=a[x].ry;i++)
if(mapp[a[x].lx][i]!=0) return 0;//如果上面有一格没涂就不能涂这格
return 1;
}
bool cmp(node x,node y){
if(x.ry==y.ry)
return x.ry<y.ry;
else return x.lx<x.rx;
}
void dfs(int now,int sum,int cnt)
{
if(sum>ans) return;//最优性剪枝
if(cnt==n)//当所有矩形都涂色完成
{
ans=sum; //不用取最小值 因为前面的剪枝已经把大于最优解剪掉了
return;
}
for(int i=1;i<=n;i++)//枚举矩形
{
if(!a[i].vis&&pd(i))//如果这个矩形没有被涂色 且可以涂
{
if(a[i].col==a[now].col&&pd(i))//当颜色一样时
{
a[i].vis=1;
add(i);
dfs(i,sum,cnt+1);//拿笔数不用加1 而已涂矩形要加1
a[i].vis=0;
del(i);
}
if(a[i].col!=a[now].col&&pd(i))//当颜色不一样时
{
a[i].vis=1;
add(i);
dfs(i,sum+1,cnt+1);//都要加1
a[i].vis=0;
del(i);
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].lx>>a[i].ly>>a[i].rx>>a[i].ry>>a[i].col;
a[i].vis=0;
for(int j=a[i].lx+1;j<=a[i].rx;j++)
for(int k=a[i].ly+1;k<=a[i].ry;k++)
mapp[j][k]=a[i].col;//保存颜色
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(!a[i].lx&&!a[i].vis)//只要从最上面开始搜索
{
a[i].vis=1;
add(i);
dfs(i,1,1);//先取第一个
a[i].vis=0;
del(i);
}
}
cout<<ans;
}