画家问题保姆级解题思路

画家问题(题目源于北大百练)在这里插入图片描述

在这里插入图片描述

在这里提供几组测试数据

在这里插入图片描述
在这里插入图片描述

下面进行解题环节

解题算法思路:
我们应该找到一种涂色的规律,以及一种检验是否涂色成功的方法。
所以,这种涂色规律就是在待检测的画板的最上方另外新加一行,这一行进行枚举,枚举的类型可利用二进制数字的特性罗列。然后在每一个枚举的类型下进行检测,从最上面这一行开始,只要有未涂色的方块,那么一定要对这个方块的下一行进行涂色操作,依次类推,直到将倒数第二行全部变成有颜色的方块。
这个时候,涂色任务结束,我们要去判断,在这种枚举的情况下我们能否把画板涂满,就是对最后一行进行逐个检测,如果全部为黄色,就证明涂色成功。然后,对下一个枚举类型进行检测相同检测。

下面进行代码解读环节

1.主函数区域

int main(){
	//信息初始化 
	int n;
	cin >> n;
	//scanf("%d",&n);  //这是画板的行列数
	//getchar();       一定要吸收空格,否则就会读入到数组中 
	//创建二维数组储存初始画板信息,将w,y字母转化成0,1数字 
	char huaban[20][20]; 
	for(int i = 1;i<=n;i++){   //留出第一行用来枚举 
		for(int j =0;j<n;j++){
			//scanf("%c",&huaban[i][j]);
			cin >>  huaban[i][j];
			huaban[i][j]=(huaban[i][j]=='w')?'0':'1';
		}
		//getchar();  //吸收回车字符 
	}
	//补充二进制0,1枚举行
	Enum(huaban,n);        //进行枚举操作,其他操作步骤都在这个函数中调用
	return 0;
}

这一段代码不难看懂,需要强调说明的一点,也是我在做题过程中迟迟没有通过的原因!!!!!!!!!!
在平台的测试系统中,我一开始利用getchar去吸收回车字符,但是在测试中可能进入了缓冲区,并不能按照我的想法进行操作,具体原因我没有明白,也希望有大佬可以帮助我,留言在评论区,万分感激!
最后我是采用了cin进行输入,利用它自动换行的特性,终于成功ac。

2.枚举区域和最终的输出

void Enum(char huaban[20][20],int n){
	int chance = (int)pow(2,n);                    //这是枚举数据   利用十进制转换二进制
	int num[1234]={0};//涂色次数储存器 
	char huaban1[20][20];
	int yy = 0;        
	for(int i = 0;i < chance; i++){
		//十进制转二进制放到huaban第一行中 
		char bu[20];
		for(int oi = 0;oi < 20; oi++)	bu[oi]='0';           //可以保存0字符在数组
		reverse(bu,i,n);
		for(int k = 0;k < n;k++){
			huaban1[0][k]=bu[k];  
		} 
		//在这一次的情况下去判断能否完成涂色任务并且记录步数
		//把huaban1内的值保留一份利于每一种情况的使用
		for(int i = 1;i <= n;i++){   //留出第一行用来枚举 
		for(int j = 0;j < n;j++){
			huaban1[i][j]=huaban[i][j];
		}
		}
//输出区域
		int t=task(huaban1,n);     //在函数中huaban1传回保留值,每次无法恢复初始状态 
		if(t >= 0) {
			num[yy]=t;
			yy++;
		} 
	}
	//看num中的最小值
	if(yy == 0)
		printf("inf");
	else
		printf("%d",min(num,yy)); 
}

在这一块需要特别说明的就是进行涂色操作时要把原始画板数据放到一个新的数组中huaban1,因为数组传参在涂色操作时改变画板颜色,并进行保留,所以每次都要将原始画板数据huaban复制到待操作画板huaban1中

3.画板补充的第一行枚举数据

void reverse(char *bu,int i,int n){
	int z = 0;
	while(i){
		bu[z]=i%2+'0';
 		i/=2;
		z++;
	}
}

因为已经把bu数组中全部赋值为‘0’,所以没有改变的地方默认为‘0’,此外bu数组中的前后顺序与二进制的真实数据顺序是否相同没有用,因为所有情况都会罗列。

4.进行涂色任务

int task(char huaban1[20][20],int n){    //注意此时是n+1行,n列
	//开始逐行去检索没涂色的地方 
	//如果没涂色,对下一行进行涂色操作
	int count = 0;//涂色计数器 
	for(int zz=0;zz<n;zz++){  //最后一行不用涂色操作直接判定即可 
		for(int hh=0;hh<n;hh++){
			if(huaban1[zz][hh]=='0'){
				//进行涂色
				if(hh==0){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					if((zz+2)!=(n+1))
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
				}
				else if(hh==(n-1)){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					if((zz+2)!=(n+1))
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
				} 
				else if(zz==(n-1)){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
				}
				else{
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
				}
				count++;
			} 
		}
	} 
	//涂色结束,去判断最后一行是否全为1
	int op;
	for(op = 0;op<n;op++){
		if(huaban1[n][op]=='1')
			continue;
		else
			return -1;
	} 
	return count;
}

这个地方我必须承认,涂色的操作确实有些麻烦了,这里之所以呈现出来,是想提醒各位,只要在写代码时这种机械性重复动作,一是要考虑下循环能不能用,二是要考虑我写个函数能不能代替,所以有简化版的涂色操作在文章最后面。

5.返回最小的操作步骤

int min(int num[],int yy){
	int min1=num[0];
	for(int ji = 1;ji < yy;ji++){
		if(num[ji]<min1){                
			min1=num[ji];
		}
	}
	return min1;
}

这个地方我也得承认,多余了,因为可以不用数组去储存这个画板的所有成功涂色步骤数,而是每次有步骤数值,就去比较,只保留最小值,直接进行输出。

呈现全部代码(较为多余版)
#include<stdio.h>
#include<math.h>
#include <cstring>
#include <iostream>
using namespace std;
int min(int num[],int yy);
int task(char huaban1[20][20],int n);
void reverse(char *bu,int i,int n);
void Enum(char huaban[20][20],int n); 

int main(){
	//信息初始化 
	int n;
	cin >> n;
	//scanf("%d",&n);  //这是画板的行列数
	//getchar();       //一定要吸收空格,否则就会读入到数组中 
	//创建二维数组储存初始画板信息,将w,y字母转化成0,1数字 
	char huaban[20][20]; 
	for(int i = 1;i<=n;i++){   //留出第一行用来枚举 
		for(int j =0;j<n;j++){
			//scanf("%c",&huaban[i][j]);
			cin >>  huaban[i][j];
			huaban[i][j]=(huaban[i][j]=='w')?'0':'1';
		}
		//getchar();  //吸收回车字符 
	}
	//补充二进制0,1枚举行
	Enum(huaban,n);
	return 0;
}

void Enum(char huaban[20][20],int n){
	int chance = (int)pow(2,n);
	int num[1234]={0};//涂色次数储存器 
	char huaban1[20][20];
	int yy = 0;        
	for(int i = 0;i < chance; i++){
		//十进制转二进制放到huaban第一行中 
		char bu[20];
		for(int oi = 0;oi < 20; oi++)	bu[oi]='0';           //可以保存0字符在数组
		reverse(bu,i,n);
		for(int k = 0;k < n;k++){
			huaban1[0][k]=bu[k];  
		} 
		//在这一次的情况下去判断能否完成涂色任务并且记录步数
		//把huaban1内的值保留一份利于每一种情况的使用
		for(int i = 1;i <= n;i++){   //留出第一行用来枚举 
		for(int j = 0;j < n;j++){
			huaban1[i][j]=huaban[i][j];
		}
		}

		int t=task(huaban1,n);     //在函数中huaban1传回保留值,每次无法恢复初始状态 
		if(t >= 0) {
			num[yy]=t;
			yy++;
		} 
	}
	//看num中的最小值
	if(yy == 0)
		printf("inf");
	else
		printf("%d",min(num,yy)); 
}

void reverse(char *bu,int i,int n){
	int z = 0;
	while(i){
		bu[z]=i%2+'0';
 		i/=2;
		z++;
	}
	//反转补位
	//for(int k = 0,j = (n-1);k < j;k++,j--){
	//	char temp = bu[k];bu[k]=bu[j];bu[j]=temp;
	//}
}

int task(char huaban1[20][20],int n){    //注意此时是n+1行,n列
	//开始逐行去检索没涂色的地方 
	//如果没涂色,对下一行进行涂色操作
	int count = 0;//涂色计数器 
	for(int zz=0;zz<n;zz++){  //最后一行不用涂色操作直接判定即可 
		for(int hh=0;hh<n;hh++){
			if(huaban1[zz][hh]=='0'){
				//进行涂色
				if(hh==0){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					if((zz+2)!=(n+1))
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
				}
				else if(hh==(n-1)){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					if((zz+2)!=(n+1))
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
				} 
				else if(zz==(n-1)){
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
				}
				else{
					huaban1[zz][hh]=huaban1[zz][hh]=='0'?'1':'0';
					huaban1[zz+1][hh]=huaban1[zz+1][hh]=='0'?'1':'0';
					huaban1[zz+1][hh-1]=huaban1[zz+1][hh-1]=='0'?'1':'0';
					huaban1[zz+1][hh+1]=huaban1[zz+1][hh+1]=='0'?'1':'0';
					huaban1[zz+2][hh]=huaban1[zz+2][hh]=='0'?'1':'0';
				}
				count++;
			} 
		}
	} 
	//涂色结束,去判断最后一行是否全为1
	int op;
	for(op = 0;op<n;op++){
		if(huaban1[n][op]=='1')
			continue;
		else
			return -1;
	} 
	return count;
}

int min(int num[],int yy){
	int min1=num[0];
	for(int ji = 1;ji < yy;ji++){
		if(num[ji]<min1){                
			min1=num[ji];
		}
	}
	return min1;
}
补充代码的优化方案

关于涂色操作的简化:

for(int zz=0;zz<n;zz++){  //最后一行不用涂色操作直接判定即可 
		for(int hh=0;hh<n;hh++){
			if(huaban1[zz][hh]=='0'){
				//进行涂色
				    Easytask(zz+1,hh , n , huaban1);//以(zz+1,h)为涂色中心
					count++;
			} 
		}
	} 
//再写一个新函数进行涂色操作
void Easytask(int i, int j , int n, char huaban1[20][20]){
		change(i , j , n , huaban1);
		change(i , j + 1, n ,huaban1);
		change(i , j - 1 , n ,huaban1);
		change(i - 1 , j , n ,huaban1);
		change(i + 1 , j , n ,huaban1);
}

//再加一个函数进行判断该区域是否越界
void change(int i , int j , int n , char huaban1[20][20]){
		if(i >= 0 && i <= n && j >= 0 && j < n){    //不用分情况讨论,只要在画板里就能改变
				huaban1[i][j] = (huaban1[i][j] == '0') ? '1' : '0';
		}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
画家算法和Z缓存算法都是用来解决三维图形显示时的遮挡问题算法,但是它们的实现方式和效率有所不同。 画家算法,也称为排序算法,是一种按照深度排序的遮挡处理算法。它通过对场景中的所有多边形按照深度排序,并从前往后渲染,实现遮挡关系的确定。具体实现过程是先将所有多边形按照深度排序,然后按照顺序进行绘制,每次绘制时都会检查当前像素点的深度值与已绘制部分的深度值进行比较,如果当前像素点在已绘制部分的后面,则跳过不绘制,否则进行绘制。虽然画家算法能够处理复杂的场景,但是它的效率较低,因为每次绘制都需要进行深度值的比较。 而Z缓存算法,也称为深度缓存算法,是一种利用缓存来存储深度信息的遮挡处理算法。它通过先将场景中的所有多边形按照深度排序,并将深度值存储在缓存中,在渲染时,每次绘制一个像素时,都会将当前像素点的深度值与缓存中的深度值进行比较,如果当前像素点在已绘制部分的后面,则跳过不绘制,否则进行绘制,并更新缓存中的深度值。由于Z缓存算法利用缓存来存储深度信息,因此它的效率较高,但是需要消耗较大的内存空间。 综上所述,画家算法和Z缓存算法都是用来解决三维图形显示时的遮挡问题算法,但是它们的实现方式和效率有所不同。画家算法通过深度排序来实现遮挡关系的确定,效率较低;而Z缓存算法利用缓存来存储深度信息,效率较高,但是需要消耗较大的内存空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值