根据第一题的经验,我先力求完成第一步,找最小次数,学以致用!
初次尝试:
//冰箱开关
#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一起加油鸭!
千磨万击还坚劲,
任尔东西南北风!