枚举算法思路训练:poj1753

从知乎找到一份训练大纲,先按照这个练习吧

poj1753
大意是4×4的棋盘上,不断黑白面的翻转自己和上下左右(如果有棋子的话),如果有可能翻到只有一种颜色,那么算出来最少翻几个棋子就可以做到只有一种颜色

枚举的思想是不断地猜测,从可能的集合中一一尝试,然后再判断题目的条件是否成立。
oiwiki是这么说的。。。。

不过“从可能的集合中一一尝试”这段话引起了我的注意,原因后面再说
先说一下我的思路吧:

既然是4×4的棋盘,那么翻棋子最多可能就是16个棋子每个棋子都翻一遍
因为一个棋子翻两遍等于没翻

所以最初的想法就是直接把从一个棋子不动到16个棋子都翻的情况一个个看一遍

一共列出了2的16次方个可能性
(每个棋子有翻与不翻两种状态,一共16个棋子)

我用的方法是dfs
思路就是把每个坐标翻转与不翻转两个状态都看一下,用递归建栈的方法从(0,0)到(4,4)不断递归枚举,最后选出步数最少的情况

代码贴出来

bool dfs(int n1,int n2) {
 if (isOk())//达到所有棋子一个颜色了
  Min = Min > temp_min ? temp_min : Min;//更新Min
 if (n1 == 5)//行数到5了,不能走了
  return false;
 if (n2 == 5) {//列数到5了,转而去看下一行的第一个元素
  dfs(n1 + 1, 1);//下一层的第一个棋子
  return false;
 }//顺道一提,n1的if检查一定放在n2前面
 if (debug()) 
  a=1;
 //第一个点
 point(n1, n2);//按题目规律翻转这个坐标该翻转的棋子
 Jud[n1][n2] = true;//标记已经选择(已经翻了)
 temp_min++;//最小翻转棋子数++
 dfs(n1, n2 + 1);//列数+1
 //dfs(n1+1,n2);//加不加这个的区别看下面的图片
 point(n1, n2);//把这个坐边翻回来
 Jud[n1][n2] = false;//不选,不翻这个棋子,翻其他的
 temp_min--;//最小翻转棋子数--
 dfs(n1, n2 + 1);
 //dfs(n1+1,n2);
}

其实貌似可以先考虑不翻的情况,可以省一小部分操作的时间,算了就这样吧

在这里插入图片描述

因为一开始我是考虑了搜索下一列的同时搜索下一行,所以做了重复的操作,两个dfs变成了4个dfs

时间翻了4倍,所以留下一个猜想,是不是复杂度和递归操作数称n 2 ^2 2的关系
//编辑博客的时候打出$ ^2 $(删空格)可以出上面的效果
代码完整版如下:

#include<iostream>
using namespace std;
char jud[6][6];
bool Jud[6][6];
int Min = 0x7fffffff;
int temp_min;
int a;
void change(int n1,int n2) {
 jud[n1][n2] == 'b' ? jud[n1][n2] = 'w' : jud[n1][n2] = 'b';
}
void point(int n1,int n2) {//不用深究,
//就是把对应坐标应该翻转的棋子都翻转罢了
 int temp_n1, temp_n2;
 int n[5][2] = { {0,0},{0,1},{0,-1},{1,0},{-1,0} };
 for(int i=0;i<5;i++)
   if (jud[n1 + n[i][0]][n2 + n[i][1]])
    change(n1 + n[i][0], n2 + n[i][1]);
}
bool isOk() {//判断是不是同色
 char temp = jud[1][1];
 for(int i=1;i<=4;i++)
  for (int j = 1; j <= 4; j++) {
   if (jud[i][j] != temp)
    return false;
  }
 return true;
}
bool dfs(int n1,int n2) {
 if (isOk())//达到所有棋子一个颜色了
  Min = Min > temp_min ? temp_min : Min;//更新Min
 if (n1 == 5)//行数到5了,不能走了
  return false;
 if (n2 == 5) {//列数到5了,转而去看下一行的第一个元素
  dfs(n1 + 1, 1);//下一层的第一个棋子
  return false;
 }//顺道一提,n1的if检查一定放在n2前面
 if (debug()) 
  a=1;
 //第一个点
 point(n1, n2);//按题目规律翻转这个坐标该翻转的棋子
 Jud[n1][n2] = true;//标记已经选择(已经翻了)
 temp_min++;//最小翻转棋子数++
 dfs(n1, n2 + 1);
 point(n1, n2);
 Jud[n1][n2] = false;//不选,不翻这个棋子,翻其他的
 temp_min--;//最小翻转棋子数--
 dfs(n1, n2 + 1);
}
int main() {
	 int i, j;
	 for (i = 1; i <= 4; i++) {
	  for (j = 1; j <= 4; j++) {
	   scanf("%c", &jud[i][j]);
	  }
	  getchar();
	 }
	 dfs(1, 1);
	 if (Min == 0x7fffffff)  cout << "Impossible" << endl;
	 else    cout << Min << endl;
}

自己的思路做完搜了下别人的做法,这个比较好理解
就是说只关注第一行的四个棋子的各种情况,然后按照“如果上一行不是指定的颜色就翻转”的思路,真就是看别人思路更醍醐灌顶

这也就是我为啥关注枚举算法介绍里面“集合”俩字的关注了
仔细想来,思考问题的方式不仅仅是从开始出发,可以直接关注结果然后从中找规律,比如说翻转最多16次,还有“上一行不是我要的颜色下面就翻转”的思路都是从结果倒退过来的

.
.
.
.
.
.
.
.
.
.
.
.
.
.

Long time no see! csdn world!

闲扯一点废话
我不知道以后的路子该怎么走
我只知道
先让自己开心再说
毕竟心态好才能做其他事
不想听的就不听
不想看的就不看
管他对与错呢
对自己好点
才是实实在在的东西
有大破必有大立,
不要刻意去想以前混乱的训练方法带来的副作用了,
能记住的不会忘,
记不住的迟早学回来,
跑得快不一定赢,不跌跟头才是胜利,
至少现在从地上爬起来了
加油!明天会更好!
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值