先列出题目:

1、POJ 1753

POJ 1753  Flip Game:http://poj.org/problem?id=1753

Sample Input

bwwb
bbwb
bwwb
bwww

Sample Output

4

入手竟然没有思路,感觉有很多很多种情况需要考虑,也只能使用枚举方法才能解决了吧~

4x4的数组来进行数据存储的话操作起来肯定非常不方便,这里借用位压缩的方法来存储状态,使用移位来标识每一个位置的的上下左右的位置操作。 详细看这里。

1、当棋盘状态id为0(全白)或65535(全黑)时,游戏结束,0^1=1,1^1=0,所以翻转的操作可以通过异或操作来完成,而翻转的位置可以通过移位来确定。

2、结束标识!!!!!

分步骤:

1、从输入到位标识状态:

 1     int state = 0;
 2     char ch[4];
 3
 4     while(cin>>ch)
 5     {
 6        for(int j = 0 ; j < 4 ; j++)
 7         {
 8             state = state<<1;
 9             if(ch[j] == 'b')
10                 state += 1;
11         }
12     }

2、从一个状态到下一个状态的的转换,比如对应位置i处进行改变后得到的状态:

这里的16个数据使用代码生成可能是想不到的,但是可以通过手动变换一次获得~~~

其实相当于

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

在每一个位置进行变换后的状态记录,正好对应疑惑的16个数据

 1 int change[16] ={
 2      51200,58368,29184,12544,
 3      35968,20032,10016,4880,
 4      2248,1252,626,305,
 5      140,78,39,19
 6 };
 7
 8 state = state^change[i];//对应第i个位置的改变
 9
10 //上面的16个数据是由下面的代码得来的
11
12 int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
13 void init()
14 {
15     int i,j,x,y,t,temp;
16     for(i=0;i<4;++i)
17     {
18         for(j=0;j<4;++j)
19         {
20             temp = 0;
21             temp ^= (1<<((3-i)*4+3-j));   //第一行代表16位的高4位,同理第一列也代表高位,所以棋盘(i,j)处在16位中的位置是((3-i)*4+3-j)
22
23             for(t=0;t<4;++t)
24             {
25                 x = i + dir[t][0];
26                 y = j + dir[t][1];
27                 if(x<0 || y<0 || x>3 || y>3)
28                     continue;
29                 temp ^= (1<<((3-x)*4+3-y));
30             }
31             cout<<temp<<" ";
32         }
33         cout<<endl;
34     }
35 }

3、判断是否满足情况只需要判断state == 0 || state == 65535 即可。

4、解决了小问题,再思考一下大逻辑:

初始状态即为纯色则直接输出0,初始不是纯色,翻转一个的状态是纯色,则输出1,翻转一个会产生n种状态,0<=n<=16,这些状态要放入到队列中进行保存,再从中出队列再进行下一次的翻转。关键问题是什么情况下判定翻转结束,仍然没有纯色出现,则输出impossible,大于2次的翻转的初态都是从队列中取出来的,必须提前设计一个状态标识,表明同一种状态不再次进行入队列,那么当队列为空时才可以下结论。

 1 //需要设置一个是否处理过的标识,共有状态有65536个,即0-65535,
 2 //如果对应位置被标记为1了,则说明已经处理过,直接抛弃进行下一次
 3
 4 //这样的遍历过程被称为BFS的过程
 5
 6
 7 bool visit[65536];
 8
 9 int bfs(int state)//返回值是进行的步数,impossible为-1
10 {
11     queue<Node> q;
12     Node current,next;
13
14     current.state = state;
15     current.step = 0;
16     q.push(current);
17     visited[state] = true;
18
19     while(!q.empty())
20     {
21         current = q.front();
22         q.pop();
23
24         if(current.state == 0 || current.state == 65535)
25             return current.step;
26
27         for(int i = 0;i<16;i++)//每一种状态都要进行16次操作
28         {
29             next.state = current.state^change[i];
30             next.step = current.step+1;
31
32             if(next.state == 0 || next.state == 65535)
33                 return next.step;
34             else
35             {
36                 if(visited[next.state])
37                     continue;
38                 else
39                 {
40                     visited[next.state] = true;
41                     q.push(next);
42                 }
43             }
44         }
45     }
46     return -1;
47 }

总结:枚举,此题就是使用枚举的思想来找到需要的解,尤其是使用队列来进行一个的while循环来进行的。

这里记录一下全部代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include<queue>
 4
 5 using namespace std;
 6
 7 int change[16] ={
 8      51200,58368,29184,12544,
 9      35968,20032,10016,4880,
10      2248,1252,626,305,
11      140,78,39,19
12 };
13
14 struct Node
15 {
16     int state;
17     int step;
18 };
19 int state = 0;
20 bool visited[65536];
21
22 int bfs(int state)//返回值是进行的步数,impossible为-1
23 {
24     queue<Node> q;
25     Node current,next;
26
27     current.state = state;
28     current.step = 0;
29     q.push(current);
30     visited[state] = true;
31
32     while(!q.empty())
33     {
34         current = q.front();
35         q.pop();
36
37         if(current.state == 0 || current.state == 65535)
38             return current.step;
39
40         for(int i = 0;i<16;i++)//每一种状态都要进行16次操作的
41         {
42             next.state = current.state^change[i];
43             next.step = current.step+1;
44
45             if(next.state == 0 || next.state == 65535)
46                 return next.step;
47             else
48             {
49                 if(visited[next.state])
50                     continue;
51                 else
52                 {
53                     visited[next.state] = true;
54                     q.push(next);
55                 }
56             }
57         }
58     }
59     return -1;
60 }
61
62 int main()
63 {
64     char ch[4];
65     while(cin>>ch)
66     {
67        for(int j = 0 ; j < 4 ; j++)
68         {
69             state = state<<1;
70             if(ch[j] == 'b')
71                 state += 1;
72         }
73     }
74
75     memset(visited,false,sizeof(visited));
76     int count = bfs(state);
77     if(count == -1)
78         cout<<"Impossible";
79     else
80         cout<<count;
81     return 0;
82 }

View Code

下一个类似的题目是POJ的2965题目:The Pilots Brothers' refrigerator http://poj.org/problem?id=2965

Sample Input

-+--
----
----
-+--

Sample Output

6
1 1
1 3
1 4
4 1
4 3
4 4

分析:与上面的类似,需要解决的分步骤有如下几个:

1、一次状态改变的对应异或状态,共有16个

2、每一个Node包括状态值、进行的步骤以及达到此状态之前进行的步骤序列

3、成功的标识是全部都变成减号,即冰箱门打开。

分步骤:

1、生成16个异或数据+对应0,-对应1,每次进行同行同列的反转

相当于

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

在每一个位置进行变换后的状态记录,正好对应异或的16个数据

1 int change[16] ={
2       63624, 62532, 61986, 61713,
3       36744, 20292, 12066, 7953,
4       35064, 17652, 8946, 4593,
5       34959, 17487, 8751, 4383
6   };

2、给出Node的定义,包括三个数据

1 struct Node
2 {
3     int state;
4     int step;
5     string seq;//使用string可以append操作步骤,每次添加2个,最后当成数组读出来即可
6 };

3、这里先使用与上面的方法一致的策略得出结果,可以得出正确结果,但是超时,超时,超时,Time Limit Exceeded!!!

代码在这里,思路与上面的题目一致!

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include<queue>
  4 using namespace std;
  5
  6
  7 int change[16] ={
  8       63624, 62532, 61986, 61713,
  9       36744, 20292, 12066, 7953,
 10       35064, 17652, 8946, 4593,
 11       34959, 17487, 8751, 4383
 12   };
 13
 14 struct Node
 15 {
 16     int state;
 17     int step;
 18     string seq;
 19 };
 20 int state = 0;
 21 bool visited[65536];
 22
 23 string int2str(int num)
 24 {
 25     if(num == 0)
 26         return "0";
 27     string str = "";
 28     int num_ = num > 0 ? num : -1 * num;
 29     while(num_)
 30     {
 31         str = (char)(num_ % 10 + 48) + str;
 32         num_ /= 10;
 33     }
 34     if(num < 0)
 35         str = "-" + str;
 36     return str;
 37 }
 38
 39 Node bfs(int state)//返回值是进行的步数,impossible为-1
 40 {
 41     queue<Node> q;
 42     Node current,next;
 43
 44     current.state = state;
 45     current.step = 0;
 46     current.seq ="Z";
 47
 48     q.push(current);
 49     visited[state] = true;
 50
 51     while(!q.empty())
 52     {
 53         current = q.front();
 54         q.pop();
 55
 56         if(current.state == 65535)
 57             return current;
 58
 59         for(int i = 0;i<16;i++)//每一种状态都要进行16次操作的!!!!!
 60         {
 61             next.state = current.state^change[i];
 62             next.step = current.step+1;
 63             //这里添加上操作的步骤序列
 64             next.seq = "Z";
 65             string tmpi = int2str(i/4 + 1);
 66             string tmpj = int2str(i%4 + 1);
 67
 68             next.seq = current.seq;
 69             next.seq.append(tmpi);
 70             next.seq.append(tmpj);
 71
 72             if(next.state == 65535)
 73                 return next;
 74             else
 75             {
 76                 if(visited[next.state])
 77                     continue;
 78                 else
 79                 {
 80                     visited[next.state] = true;
 81                     q.push(next);
 82                 }
 83             }
 84         }
 85     }
 86     current.step = -1;
 87     return current;
 88 }
 89
 90 int main()
 91 {
 92     freopen("data.in", "r", stdin);
 93
 94
 95
 96     char ch[4];
 97     while(cin>>ch)
 98     {
 99        for(int j = 0 ; j < 4 ; j++)
100         {
101             state = state<<1;
102             if(ch[j] == '-')
103                 state += 1;
104         }
105     }
106
107     Node count = bfs(state);
108     if(count.step != -1)
109     {
110         cout<<count.step<<endl;
111         for(int tt = 1;tt<count.seq.length();tt+=2)
112         {
113             cout<<count.seq[tt]<<" "<<count.seq[tt+1]<<endl;
114         }
115     }
116
117     fclose(stdin);
118     return 0;
119 }

View Code

4、需要重新分析题目逻辑,是不是哪里的分析啰嗦了,导致占用了过多的耗时!!!

题目的操作是整行整列翻转,所以按照每一个位置进行操作会产生很多的冗余操作,看这里的分析:(感谢原作者hackbuteer1)

  1. 先看一个简单的问题,如何把'+'变成'-'而不改变其他位置上的状态?答案是将该位置(i,j)及位置所在的行(i)和列(j)上所有的handle更新一次,
  2. 结果该位置被更新了7次,相应行(i)和列(j)的handle被更新了6次,剩下的被更新了4次.
  3. 被更新偶数次的handle不会造成最终状态的改变.因此得出高效解法,在每次输入碰到'+'的时候自增该位置与相应的行和列,当输入结束后,遍历数组,所有为T的位置则是操作的位置, 而T位置的个数之和则是最终的操作次数.

这里做一个理解:就是最终单纯的将一个位置由+变成-,只需要将它本身改变一次即可,其它相当于状态不变。

上面的方法可能不好理解,可以考虑另外一种方法:即使用深度搜索的方法。

代码没能够完全调试通过,先放一下,后面再回来学习。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include<queue>
 4 using namespace std;
 5
 6
 7 int change[16] ={
 8       63624, 62532, 61986, 61713,
 9       36744, 20292, 12066, 7953,
10       35064, 17652, 8946, 4593,
11       34959, 17487, 8751, 4383
12   };
13
14
15
16 int state = 0;
17 bool flag=false;
18 bool visited[65536];
19
20 int step;
21 int ri[16],cj[16];
22
23
24 void dfs(int bit, int deep)//深度遍历
25 {
26     if(deep==step)
27     {
28         flag = (state == 65536);
29         return;
30     }
31
32     if(flag || bit>15)
33         return;
34
35     ri[deep]=bit/4;
36     cj[deep]=bit%4;
37
38     state = state ^ change[bit];
39     dfs(bit+1,deep+1);
40
41     state = state ^ change[bit];
42     dfs(bit+1,deep);
43
44     return;
45 }
46
47 int main()
48 {
49     freopen("data.in", "r", stdin);
50
51
52
53     char ch[4];
54     while(cin>>ch)
55     {
56        for(int j = 0 ; j < 4 ; j++)
57         {
58             state = state<<1;
59             if(ch[j] == '-')
60                 state += 1;
61         }
62     }
63
64     for(step = 0;step<=16;step++)//共16个操作点,按照每一个操作点进行深度遍历
65     {
66         dfs(0,0);
67         if(flag)//满足条件直接跳出
68             break;
69     }
70
71
72     //输出
73     cout<<step<<endl;
74     for(int tt = 0;tt<step;tt++)
75     {
76         cout<<ri[tt]+1<<" "<<cj[tt]+1<<endl;
77     }
78
79
80
81     fclose(stdin);
82     return 0;
83 }

View Code


学习总结:针对枚举法的总结

枚举法就是从可能的解中一一进行枚举,并使用给定的条件进行判定,找到满足条件的一个或者全部解即结束。枚举法本质上属于搜索的算法。枚举法的特点是算法简单,对于可确定的解的值域但是又没有很好地算法可以解决时就可以考虑使用枚举法,比较原始,运算量大,算法的时间复杂度是指数级别的,一定注意考虑在小范围内局部中使用枚举法效率会高一些!!!

另外针对广度优先BFS和深度优先DFS两个算法进行学习,后面的搜索算法,图搜索等二叉树搜索等会经常用到,需要深刻学习!!!

转载于:https://www.cnblogs.com/yushuo1990/p/5967724.html

[ACM训练] 算法初级 之 基本算法 之 枚举(POJ 1753+2965)相关推荐

  1. 从 ACM 训练领悟坚持之道

    作者 | 梁唐 责编 | 八宝粥 来源 | TechFlow(id:techflow2019) 今天的主题和休息有关,不知道各位前行当中的小伙伴是否会有这样一刻,好像自己突然就没了力气,有些努力不动了 ...

  2. 算法岗诸神黄昏,算法初级职位内卷,如何选择适合自己的方向?

    AI学习社区 点击右侧关注,我们一起学AI! ☞[加薪必备]全套零基础学AI资料免费领! 编辑:Amusi 直接来源:CVer https://www.zhihu.com/question/34374 ...

  3. Acm+java各种数据结构_Acm竞赛算法——数据结构算法分类

    ACM竞赛的算法,分为了数学.数据结构和算法三大块. 一 数学(Mathematics) 1 离散数学(Discrete Mathematics) 1.1 图论(Graph Theory) 图的遍历( ...

  4. 速度超Mask RCNN四倍,仅在单个GPU训练的实时实例分割算法

    这个带分割是25帧,有漏检. 速度超Mask RCNN四倍,仅在单个GPU训练的实时实例分割算法 原文:速度超Mask RCNN四倍,仅在单个GPU训练的实时实例分割算法 | 技术头条 https:/ ...

  5. CV之CNN:基于tensorflow框架采用CNN(改进的AlexNet,训练/评估/推理)卷积神经网络算法实现猫狗图像分类识别

    CV之CNN:基于tensorflow框架采用CNN(改进的AlexNet,训练/评估/推理)卷积神经网络算法实现猫狗图像分类识别 目录 基于tensorflow框架采用CNN(改进的AlexNet, ...

  6. ML之NB:(NLP)基于sklearn库利用不同语种数据集训练NB(朴素贝叶斯)算法,对新语种进行语种检测

    ML之NB:(NLP)基于sklearn库利用不同语种数据集训练NB(朴素贝叶斯)算法,对新语种进行语种检测 目录 输出结果 训练数据集 设计思路 核心代码 输出结果 测试01:I love you ...

  7. 谷歌大规模机器学习:模型训练、特征工程和算法选择 (32PPT下载)

    本文转自:http://mp.weixin.qq.com/s/Xe3g2OSkE3BpIC2wdt5J-A 谷歌大规模机器学习:模型训练.特征工程和算法选择 (32PPT下载) 2017-01-26  ...

  8. 【算法-初级-数组】两数之和(JavaScript实现)

    [算法-初级-数组]两数之和(JavaScript实现) 博客说明 文章所涉及的部分资料来自互联网整理,当然还有自己个人的总结和看法,分享的目的在于共建社区和巩固自己.引用的资料如有侵权,请联系本人删 ...

  9. 算法初级_Question7_最佳存款方案(java实现)

    这篇文章讲述的是算法初级部分的最佳存款方案问题的java实现,参考的书籍为清华大学出版社出版,贾蓓等编著的<c语言趣味编程1000例>,如有错误或者不当之处,还望各位大神批评指正. 问题描 ...

  10. 简单五子棋算法——初级篇

    简单五子棋算法--初级篇 前言 设计思路 算法实现 后言 进阶设计 前言 五子是中国古老的棋类之一,是老少咸宜的娱乐项目.也是人机博弈中最简单的一类,相较于围棋.象棋变化更少,算法实现起来就相对比较简 ...

最新文章

  1. swoole使用 常用案例
  2. 使用Skywalking实现全链路监
  3. adb devices指令实例讲解
  4. NLP之WordCloud:基于jieba+matplotlib库对一段文本生成词云图~~情人节最好的礼物(给你一张过去的词云图,看看那时我们的爱情)
  5. js Array.prototype.slice.call(arguments,0) 理解
  6. ENVI图像纹理提取
  7. 快逸报表多数据源解决办法以及项目多数据源切换方法
  8. 如何在Java语言编程中,如何输入一个char型字符
  9. 大数据预测(大数据核心应用)
  10. cesium雷达图_cesium添加闪烁点,雷达图(一般用于预警)
  11. android 视频添加音乐,手机怎么给视频加音乐,安卓手机软件怎么给视频添加音乐比较方便...
  12. 如何在linux下配置网络桥接?-使初学者轻松远离ping不通的烦恼
  13. cacheable注解原理_SpringBoot:缓存注解@Cacheable详解
  14. 活动选择与小船过河问题(贪心算法)
  15. swing(二十一)
  16. python设置word背景色_Java 给Word不同页面设置不同背景
  17. ubuntu删除旧的linux内核
  18. 多传感器融合定位十四-基于图优化的定位方法
  19. 一个Windows远程工具,小巧但实用,支持RDP、SSH、SFTP、FTP等多种协议
  20. Codeforces Round #618 (Div. 1)-----A. Anu Has a Function

热门文章

  1. Java web技术及应用答案_超星《Java Web应用开发技术》答案
  2. pandas根据列值读取行
  3. 图神经网络概述(3):来自IEEE Fellow的GNN综述
  4. 如何利用caffe自带的工具包绘制accuracy/loss曲线
  5. PASCAL-VOC2012数据集(vocdevkit、Vocbenchmark_release)详细介绍
  6. 2020.12.23 随笔纪念粉笔数【2020】
  7. getParameter和getAttribute区别(超详细分析)
  8. Oracle基础查询
  9. 构造方法之间如何调用?
  10. Javascript内置对象之Date对象与HTML BOM