CCF I’m stuck!
题目:
问题描述 (ccf题库需要登陆)
试题编号: | 201312-5 |
试题名称: | I’m stuck! |
时间限制: | 1.0s |
内存限制: | 256.0MB |
问题描述: |
问题描述
给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思:
'#': 任何时候玩家都不能移动到此方格; '+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; '-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格; '|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格; '.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动; 'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; 'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。 此外,玩家不能移动出地图。 请找出满足下面两个性质的方格个数: 1. 玩家可以从初始位置移动到此方格; 2. 玩家不可以从此方格移动到目标位置。 输入格式
输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。
接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个'S'和一个'T'。 输出格式
如果玩家在初始位置就已经不能到达终点了,就输出“I'm stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。
样例输入
5 5
--+-+ ..|#. ..|## S-+-T ####. 样例输出
2
样例说明
如果把满足性质的方格在地图上用'X'标记出来的话,地图如下所示:
--+-+ ..|#X ..|## S-+-T ####X |
思考:
1.正常从S点(格子)DFS,记录所有能到达的点,注意到“T”不返回,把“T”当作普通点。遇到可走的点就+1,用flagS_T保存这个数值 (下面“格子”和“点”是一个意思)
2.反向DFS,即从T点开始“逆”DFS,记录所有S可达的基础上 T也可以到达的点。用flagT_S保存这个值 (这里T可达,很重要,精髓就是“逆”,其实真实含义为,能从某点到达T,之所以这样写其实是为了大大缩小搜索复杂度, 后面有详细解释)
3.flagS_T - flagT_S 就是所求(前提走不通直接输出“i'm stuck”)
———————————————————————开始举例—————————————————————————
具体用样例说明一下写法
从T点开始,左边和右边是“S/T/+/-”,这四个都可以走,这才是真正的“逆”和“反”。
下边,是“|” “+” “S/T”四个都可以走
上边,是“|” “+” “.” “S/T”都可以走。 (这里“.”不同)
即每走一步,下边怎么走,就想,你是否能到我,从下一步格子的角度去想,能到我,我才去走的意味。
和正向不同的是,正向for(0 > i > 4)重点在于我,以我为主导,我是“-”那只能for左右的点,“+”for上下左右的点。
而反向就不是for(0 - 4)这样了,重点在于对方(下一个格子能否到我),我本身不重要了,哪怕我是“-”也可以四个方向,指的是从上下左右到来我这个格子,最后在正向DFS时S能到的格子数中减去反向DFS时T也能到的。
这里要好好思考。
———————————————————————————结束举例————————————————————————————
总结:
反向DFS很巧妙!这是我没有想到的,大大缩小搜索范围,不然就得从S能到的点里一个一个DFS看能否到达T。反向DFS说的是从T开始,但其实真正意义在于找出,从某个点能到T的,这也是上面一直说的“逆”
AC代码:
1 #include<cstdio>
2 #include<cstring>
3 char map[55][55];
4 bool S_cango[55][55];//'S'正向DFS S 可以到达的点
5 bool T_cango[55][55];//'T'反向DFS T 可以到达的点
6 int dec[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };//上下左右
7 int Sx, Sy;
8 int Tx, Ty;
9 int R, C;
10 int flagS_T = 0;
11 int flagT_S = 0;
12
13 int S_canto_T = 0;//标记从S能否到达T
14
15 void dfs_ST(int x, int y);
16 void dfs_TS(int x, int y);
17
18 int main()
19 {
20
21 while(scanf("%d%d", &R, &C) != EOF){
22 getchar();
23 for(int i = 0; i < R; i ++)
24 {
25 for(int j = 0; j < C; j ++)
26 {
27 scanf("%c", &map[i][j]);
28 if(map[i][j] == 'S')
29 {
30 Sx = i;
31 Sy = j;
32 }
33 if(map[i][j] == 'T')
34 {
35 Tx = i;
36 Ty = j;
37 }
38 }
39 getchar();
40 }
41 memset(S_cango, 0, sizeof(S_cango));
42 memset(T_cango, 0, sizeof(T_cango));
43 dfs_ST(Sx, Sy);
44 dfs_TS(Tx, Ty);
45 if(!S_canto_T)
46 printf("I'm stuck!\n");
47 else
48 printf("%d\n", flagS_T - flagT_S);
49 }
50 }
51
52 bool illegemap(int x, int y)
53 {
54 if(x >= 0 && x < R && y >= 0 && y < C)
55 return true;
56 else
57 return false;
58 }
59 void dfs_ST(int x, int y)
60 {
61 if(illegemap(x, y))
62 {
63 if(x == Tx && y == Ty){
64 S_canto_T = 1;
65 S_cango[x][y] = 1;
66 for(int i = 0; i < 4; i ++){
67 int thisx = x + dec[i][0];
68 int thisy = y + dec[i][1];
69 dfs_ST(thisx, thisy);
70 }
71 }
72 else if( (x == Sx && y == Sy) || map[x][y] == '+'){
73 if(S_cango[x][y] != 1 ){
74 flagS_T ++;
75 S_cango[x][y] = 1;
76 for(int i = 0; i < 4; i ++){
77 int thisx = x + dec[i][0];
78 int thisy = y + dec[i][1];
79 dfs_ST(thisx, thisy);
80 }
81 }
82 }
83 else if(map[x][y] == '#')
84 return;
85 else if(map[x][y] == '.'){
86 if(S_cango[x][y] != 1){
87 flagS_T ++;
88 S_cango[x][y] = 1;
89 int thisx = x + 1;
90 int thisy = y;
91 dfs_ST(thisx, thisy);
92 }
93 }
94 else if(map[x][y] == '|'){
95 if(S_cango[x][y] != 1){
96 flagS_T ++;
97 S_cango[x][y] = 1;
98 for(int i = 0; i < 2; i ++){
99 int thisx = x + dec[i][0];
100 int thisy = y + dec[i][1];
101 dfs_ST(thisx, thisy);
102 }
103 }
104 }
105 else if(map[x][y] == '-'){
106 if(S_cango[x][y] != 1){
107 flagS_T ++;
108 S_cango[x][y] = 1;
109 for(int i = 2; i < 4; i ++){
110 int thisx = x + dec[i][0];
111 int thisy = y + dec[i][1];
112 dfs_ST(thisx, thisy);
113
114 }
115 }
116 }
117 }
118 else
119 return;
120
121 }
122
123 void dfs_TS(int x, int y)
124 {
125 if(illegemap(x, y) && map[x][y] != '#'){
126
127 if(T_cango[x][y])
128 return;
129
130 if(S_cango[x][y] && map[x][y] != 'S')
131 flagT_S ++;
132 T_cango[x][y] = 1;
133
134 if(illegemap(x - 1, y) && map[x - 1][y] == '|' || map[x - 1][y] == '+' || map[x - 1][y] == '.' || map[x - 1][y] == 'S' || map[x - 1][y] == 'T')
135 dfs_TS(x - 1, y);
136 if(illegemap(x + 1, y) && map[x + 1][y] == '|' || map[x + 1][y] == '+' || map[x + 1][y] == 'S' || map[x + 1][y] == 'T')
137 dfs_TS(x + 1, y);
138 if(illegemap(x, y - 1) && map[x][y - 1] == '-' || map[x][y - 1] == '+' || map[x][y - 1] == 'S' || map[x][y - 1] == 'T')
139 dfs_TS(x, y - 1);
140 if(illegemap(x, y + 1) && map[x][y + 1] == '-' || map[x][y + 1] == '+' || map[x][y + 1] == 'S' || map[x][y + 1] == 'T')
141 dfs_TS(x, y + 1);
142 }
143 }
附录一:
如果还看不明白可以继续往下看,我的全部思考过程
想法1:
我首先想到暴力,m*n的all格子每一个都检测是否可以从S到此,从此到不了T,是就+1,最后输出,想一下是无脑搜,时间复杂度O(R*C),没有多耗时间,但是没有意义,换思路。
想法2:
再看题目是要求从S到某,从某到不了T,那我m*n格子总数-所有通路(DFS),但一想不对,所得结果也可能有根本就从S到不了某的
想法3:
比较混乱的思路去看了题解https://blog.csdn.net/qq_16234613/article/details/77506697,得到启发,搜索的结果其实就是答案(正好记录了格子数),但是没有去按照题解的思路,而是自己思考搜索(BFS/DFS)。如下图:
具体就是假如网格只有粉黄蓝绿4种路径,其他格子都是“#”,其中粉色的通路10步到达T,黄色5,蓝色3,绿色是6步从S到T
那我输出点的话应该输出8(黄线有5个点,蓝1个点,绿色2个)不能重复
问题在于,我怎样计算、区分这些点?
打算从S点DFS,遇到一个格子就+1,但如果遇到了T,就return,“追本溯源”,把所有这条路径上走过的格子都“销毁”,因为求的是S能到某个格子,而某个格子到不了T,所以要怎么清除所有之前所走的能到达这里的路径格子?感觉写起来不好写,卡住了。
想法4:
看了一下大家的题解,好像都是说,从起点可以到达的点里减去从终点不能到达的点,那这么说来和最开始想的暴力差不多是一个思路,只不过反向搜索,大大缩小范围,之前想偏了,复杂了。但是感觉这种一个一个不断判断排除自己思维中错误思路想法的过程很重要,这样以后才能看到题最先就想到正解。
实际写错误1:
先写一下地图,结果好久不练习,一个输入地图就调试这么久
1 #include<cstdio>
2 char map[55][55];
3 char a[55][55];
4 int main()
5 {
6 int R, C;
7 scanf("%d%d", &R, &C);
8 getchar();
9 for(int i = 0; i < R; i ++){
10 for(int j = 0; j < C; j ++){
11 scanf("%c", &map[i][j]);
12 // printf("%c", map[i][j]);
13 }
14 getchar();
15 }
16 for(int i = 0; i < R; i ++){
17 for(int j = 0; j < C; j ++)
18 printf("%c", map[i][j]);
19 printf("\n");
20 }
21
22 }
实际写错误2:
移动之后没有判断是否在map内
逻辑错误,移动后没有检查合法否,应该直接加入DFS进入判断(往下深入),在下一次判断合法则赋值和加1之类的。而不是在本次就直接赋值和加1
实际写错误3:
眼瞅着就变值了(见致命错误)
此错误指的是二维数组整错了。
一开始写的是全局0值int a[55][2],因为我以为一共是55行,每行只有xy两个值,
所以在a[3][0]赋值1之后,需要用到a[2][2]时,输出a[2][2]也是1了。其实是数组越界?
正确写法应该是a[55][55],因为第二个数字不是维度个数,而是y的坐标,那坐标(一个大地图)肯定不止有两列。
实际写错误4:
理解题意出错
一直以为 a[0[0]如果是‘-’,a[0][1]是‘|’,从a[0][0]到不了a[1][1],(其实正常写就行,这里没什么问题,只是主观以为过不去),但其实是可以过去的,对于给定样例,我的flagS_T是14,T_S是12。
所以这次的错误是正向DFS就错了。
这里也就解释了我刚开始的疑惑,
玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动
也就是T不能作为边界,以往写题都是到达T作为一个return,而这里T只是一个普通格子处理。因为我认为到T再往下走,对于应该输出“im stuck”的情况没有影响,对于能从S到达,但却到达不了T的所求结果也没有什么影响。但是也不然,具体就是样例的那个第5行第5列的‘.’,原因就不啰嗦了。
实际写错误5:
if(S_cango[x][y] == 1)
flagT_S++;
这个xy顺序是否正确,用不用交换。注意,不用,因为S_cango[x][y]指的是值为1表示能从S到达(x,y)点,反之为0则不能。所以不需要反。
实际写错误6:
反向DFS时,‘.’要反一下,但具体没理会到精髓,所以写错了
提交1:
感觉都对了第一次提交10分,而且不能查看提交
提交2:
修改了
if(S_cango[x][y] != 1)
flagT_S++;
为
if(S_cango[x][y] == 1)
flagT_S++;
注:我写的正反DFS都是只记录起点格子,不记录终点格子,即正向DFS记录S,不记录T。反向DFS记录T,不记录S。
提交3:
反向DFS错了
其实T反向DFS时,到S也可以走,一样的道理
提交4:
30分
码补丁思维,好丑好残,改得好乱、
修改后50分
发现i后台只有一组‘m stuck数据
附录二:
过程中还想对拍了,写了半天写不下去了,感觉思维本身有问题,应该好好想或者看其他人代码,而不是去写对拍,除非怎样都找不出来问题所在并且分数为80+只有少数点不行,而不是像我,10分就去对拍,本身思路就不对,对拍没什么意义,随便想一个例子就是反例,对着自己代码一行一行想,为什么不对,必要时加printf来检测(我不会所谓的debug,有空去学。。)
还有很多没写出来,先这样吧。。想清楚再看题解,别乱对拍,即使保存,及时记录错误。
我也不知道为啥AC完了再写还写不出来的样子,自己都忘了前几天咋写的了。。。
只能根据回忆写几个重要的,因为复制粘贴后代码不见了。加号成死的了。
(请忽略)
写题过程中查的东西:
多个页面 https://www.one-tab.com/page/lGpJdA9oQPaNNRZY3Ai-bQ
深搜判断一个点是否可以到达终点https://recomm.cnblogs.com/blogpost/7073439
深搜广搜区别https://www.cnblogs.com/Annetree/p/7199358.html
https://www.cnblogs.com/A-FM/p/5240887.html
二维数组http://c.biancheng.net/cpp/html/2930.html
github记录代码修改过程网址https://blog.csdn.net/u011463542/article/details/87094983 以后好东西及时分类收集 下次用githistory.xyz
ccf通知http://cspro.org/Notices/
CCF认证计划日程http://cspro.org/Plans/
如何去除a数组中与b的公共部分
CCF题目:https://blog.csdn.net/tigerisland45/article/details/54429702
好久好久真的太久没写题目了,查了一下深搜广搜差别
过程遇到的代码测试

1 //#include<cstdio> 2 //void fun(int x); 3 //int main() 4 //{ 5 // int x = 10; 6 // fun(x); 7 //} 8 //int y = 10; 9 //void fun(int x) 10 //{ 11 // printf("%d\n", x + y); 12 //} 13 14 15 //#include<cstdio> 16 //int main() 17 //{ 18 // int x; 19 // for(int i = 0; i < 10; i ++){ 20 // if(i < 5 && i >= 0){ 21 // if(i + 2 == 4) 22 // continue; 23 // printf("但是%d\n", i); 24 // } 25 // } 26 //} 27 28 //#include<cstdio> 29 //int x = 0; 30 //void fun(int x, int y); 31 //int main() 32 //{ 33 // int a = 10; 34 // int b = 12; 35 // fun(a, b); 36 // printf("%d\n", x); 37 //} 38 //void fun(int a, int b) 39 //{ 40 // x = 1211; 41 // 42 //} 43 44 45 #include<cstdio> 46 int a[55][2];//致命错误!!! 47 int b[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; 48 int main() 49 { 50 a[3][0] = 1; 51 printf("%d\n", a[3][0]); 52 printf("%d\n", a[2][2]); 53 printf("%d\n", a[2][0]); 54 // printf("%d\n", b[2][3]); 55 }
二维数组都不会了
1 错误写法 2 int dec[4][4] = { {-1, 0} {1, 0}, {0, -1}, {0, 1} };//连二维数组都忘了
3 4 正确写法 5 int dec[4][2] = { {-1, 0} {1, 0}, {0, -1}, {0, 1} };//连二维数组都忘了 6 7 其实第一种写法正确,是数组里的省略写法,但在此处我没想省略,一共就4个方向,所以【4】【2】才对
-1 0
1 0
0 -1
0 1
这样子,4*2的矩阵
好久没打开codeblock,{}回车后不自动缩进了,链接https://blog.csdn.net/qwb492859377/article/details/46933185,
Java那个我用的是AIIman。更后退出重进、
(这里很奇怪,需先smart和brace都不选,保存退出,再勾选smart,保存退出,再勾选brace才行)
每次都是按一个‘{’,出现{}},重新下载20版本的。设置完还是一样。最后发现要用巨硬(微软)的自带英文输入法,掌心输入法英文会出问题
中文注释乱码
settings->Editor->gernal settings
encoding setting
https://blog.csdn.net/softman11/article/details/6121538
哎,好烦啊,好几次提交,10分30分,写了好几天,一直玩了。https://mubu.com/doc3wnYoDFFIX4
每次都插入了提交代码,想到时候回顾,可是博客园这个编辑器挺差的,不能自动保存,以至于昨晚写的,只能这样静态待着,不会自动save,我点个标签前进(虽然好像提示我了是否离开),再回来就没了。今天重写吧,结果我想试一下前进试着找回,我全选复制,回来再粘贴,结果太气人了,所有的这种折叠代码全部变成了死的,点加号点不开了,很多提交错误和思考过程还有反例都没了。。。。哎不会html这些可能是常识吧。。。太菜了,整个v2就我不会。我会弥补的!!!还有不知道是不是我全选更改字体大小的原因,我的超链接全都不是蓝色了,看着都不知道是超链接,很不方便。哎ccf还不能查看提交。我的博客园预览感觉效果很不好,不方便看,但是没办法,有空再修改样式吧。
没有了活下去的意义。。爸爸wanqi都在乐观吧。。
哎,写这么多。。。弯路到底值不值?
二战在家。。。。一直逃避,堕落,没有或者的意义,三站。工作?不想行动。已经5/15了。。。