C++ 2.吃雪糕吗

在这里插入图片描述在这里插入图片描述
根据第一题的经验,我先力求完成第一步,找最小次数,学以致用!
初次尝试:

//冰箱开关
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int l[4][4];
int Min=0x7fffffff;
int check()
{
	int i,j;
	for(i=0;i<4;i++){
		for(j=0;j<4;j++){
			if(l[i][j]=='+'){
				return -1;
			}
		}
	}
	return 1;
}
void Switch(int m,int n){
	l[m][n]=!l[m][n];
	for(int i=0;i<4;i++){
		l[m][i]=!l[m][i];
	}
	for(int j=0;j<4;j++){
		l[j][n]=!l[j][n];
	}
}
void dfs(int s,int q){
	int x,y;
	if(s==16){
		if(check()==1){
			if(q<Min){
				Min=q;
			}
		}
		return;
	}
	else{
		x=s/4;
		y=s%4;
		dfs(s+1,q);
		Switch(x,y);
		dfs(s+1,q+1);
		Switch(x,y);
	}
}
int main()
{
	char p;
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			cin>>p;
			if(p=='-'){
				l[i][j]=1;
			}
			else{
				l[i][j]=0;
			}
		} 
	}
	dfs(0,0);
	if(Min==0x7fffffff){
		cout<<"Impossible\n"<<endl;
	}
	else{
		cout<<Min<<endl;
	}
return 0;
}
 

调试与分析:
1.一开始定义 switch() 为翻转函数,但是报错,和第一题的 min 情况相似,不能用内置的函数名定义函数或者变量,这里看一下 switch() 函数吧:
C++ switch
2.变化1:检查函数只有全为 ‘-’ 才返回 1
3.变化2:依据题意完成行列的转换
4.深搜函数:同样进行我不是很懂的迭代遍历搜索
5.看到这里,我貌似突然明白了为什么这个答案是错的了,啊,忘了说其实这段代码得不到答案,但是记录探索的过程很有意义,这段代码可以运行,但是:
在这里插入图片描述
为什么是零呢???
哈哈,我惊喜于自己很快发现了问题所在,原来我们已经将所有的 ‘-’ 与非 ‘-’ (这样讲更为精确严谨)转换为了 1 与 0 ,注意函数之间的嵌套调用关系,所以这样在调用 check() 函数时:

if(l[i][j]=='+')

这句话要真正的理解:意思是只要所有的 16 个格子内元素都不是 ‘+’ 就可以返回 1 了!所以,必须用否定句来返回 -1 !!修改为:

if(l[i][j]!=1)

这样解决了问题!!
耶:
在这里插入图片描述

接下来就是解决路径问题了,让我想一想。。。。。。
我突然想起来了什么!!深度优先搜索只能寻找最小次数,却难以找到那个最优解,这给我们的下一步操作带来了极大的不便,让我再学点知识备点货吧!!
知识点一 结构体
C++ 结构体
知识点二 惊叹号 !
一句话概括:若 a 为 True,则 ! a 为 False,反之亦然
让我们分析一下最终代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{
	int x,y;	
}coor[16];
int main(){
	char l[4][4];
	int n[4][4]={0};
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			cin>>l[i][j];
		}
	}
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			if(l[i][j]=='-'){
				continue;
			}
			n[i][j]=!n[i][j];
			for(int m=0;m<4;m++){
				n[i][m]=!n[i][m];
				n[m][j]=!n[m][j];
			}
		} 
	}
	int MIN=0;
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			if(n[i][j]){
				coor[MIN].x=i+1;
				coor[MIN].y=j+1;
				MIN++;
			}
		}
	}
	cout<<MIN<<endl;
	for(int i=0;i<MIN;i++){
		cout<<coor[i].x<<" "<<coor[i].y<<endl;
	}
}

细致分析:
1.首先创建了 16 位的数组 coor[ ] (英文‘坐标’的简写),让我们先搞懂它的内在含义,这里的16位是指对于每一个结构体变量都有16位,而不是它们的和
2.l [ i ][ j ] 用来储存输入的加减号(冰箱的开关状态)
3.(重要的思维方法,要像呼吸一样自然哦)本代码的思路是只改变关闭状态下的开关,所以接下来给出一个条件判断进行筛选:

if(l[i][j]=='-'){
	continue;
}

在遍历的过程中如果碰到打开的开关就跳过此循环进入下一次循环(continue语句),碰到关闭的开关就将它本身与和它同行同列的开关全都改变状态(!的作用),在这样的规则下遍历全部16个开关
4.接下来找最小次数,代码

if(n[i][j])

如果判断为真(非0)就代表这个开关被改变过状态,同时 MIN 刚好记录下了改变的次数,同时也记录了坐标(加一是因为要与数学坐标数值一致)
到此我有一些疑问,先分析一下:开始所有的位置都被标记为0,假设我们最后得到了答案(到现在我还不理解怎么确定遍历结束就完全打开了?)那么所有为 1 的位置一定一开始是关闭状态,也就是说
遍历过程中那些同行同列的呈打开状态的开关必定一同翻砖偶数次,而呈关闭状态的开关一同翻转后必定最终被翻奇数次,现在感觉这挺难实现的,也许我还没有理解代码,让我们再看一遍代码吧
//
又有了一些收获,我们注意一下测试样例及其输出,在纸上大致演示一下,可以发现,所走的路径正是遍历了一遍之后所有的关闭开关所在的位置!至于道理,我现在只能说暂时只可意会不可言传,也许是某方面的专业知识,容我再去慢慢探索吧!
//
我又尝试着用上述方法去翻棋子,并没能如我所愿:

//翻棋子
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
	char p[4][4];
	int q[4][4]={1};
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			cin>>p[i][j];
		}
	}
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			if(p[i][j]=p[0][0]){
				continue;
			}
			else{
				q[i][j]=!q[i][j];
				if(i-1>=0){
					q[i-1][j]=!q[i-1][j];
				}
				if(j-1>=0){
					q[i][j-1]=!q[i][j-1];
				}
				if(i+1<4){
					q[i+1][j]=!q[i+1][j];
				}
				if(j+1<4){
					q[i][j+1]=!q[i][j+1];
				}
			}
		}
	}
	int MIN1=0,MIN2=0;
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			if(q[i][j]){
				MIN1+=1; 
			}
			else{
				MIN2+=1;
			} 
		}
	}
	cout<<MIN1<<endl;
	cout<<MIN2<<endl;
	return 0;
}

调试报告:
1.多练习自己输入,不要借助任何抄写手段,这样才能进步!
2.自己写的,竟然忘了加 using namespace std,导致 cin,cout 无法被识别!
3.自己写的,错输中文字符
输出:

bwwb
bbwb
bwwb
bwww
1
15

为什么输出 1 呢???让我们输出最终的 q 数组:

1000
0000
0000
0000

成了这样子。。
就目前我朴素的世界观来看,应该这两个题的方法难以相互适用,那时事实上是什么呢?等以后的自己知识多一些了再解答吧!

越努力越幸运!
跟ZDZ一起加油鸭!
千磨万击还坚劲,
任尔东西南北风!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值