#10023. 「一本通 1.3 练习 2」平板涂色

文章讲述了如何编写程序来确定使平板涂色最少次数的方案,涉及到自动涂色机的工作原理和DFS深度优先搜索算法的应用。作者首先尝试了近似贪心的DFS方法,然后转而采用暴力解法,最终通过结合图和DFS的思路找到了正确解答。
摘要由CSDN通过智能技术生成

平板涂色

题目描述

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 1Ci20 0 ≤ x i , y i ≤ 99 0 \le x_i,y_i \le 99 0xi,yi99 1 ≤ N ≤ 16 1\le N \le 16 1N16

本人做题历程及吐槽

第一阶段,认为只要按每个矩形右下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;
} 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值