从知乎找到一份训练大纲,先按照这个练习吧
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;//更新Minif (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;//更新Minif (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!
闲扯一点废话
我不知道以后的路子该怎么走
我只知道
先让自己开心再说
毕竟心态好才能做其他事
不想听的就不听
不想看的就不看
管他对与错呢
对自己好点
才是实实在在的东西
有大破必有大立,
不要刻意去想以前混乱的训练方法带来的副作用了,
能记住的不会忘,
记不住的迟早学回来,
跑得快不一定赢,不跌跟头才是胜利,
至少现在从地上爬起来了
加油!明天会更好!