scanf输入字符的总结–百练2816红与黑

今天,在做百练OJ 2816时遇到了问题。
主要不是算法,而是细节,也让我知道了对基础的严格掌握是多么重要,而今天要说的就是scanf(“%c”,…)这个细节。
看程序:百练OJ 2816红与黑
我的代码(WA的):
//ID:百练OJ 2816红与黑
//Name:Tanky_Woo
//网站:C++奋斗乐园|C++论坛|算法论坛|ACM/ICPC论坛
//地址:http://www.cppleyuan.com/
//个人主页:www.wutianqi.com
//豆瓣小组:http://www.douban.com/group/cppleyuan/
//QQ:17611904
//QQ群:C++奋斗乐园①群:19333724(满) ②群:23840480 (满)③群:17314377 ④群:23829384
//转载请写上本帖链接及站点名称:”Tanky Woo与ACM一起做过的日子“和“C++奋斗乐园|C++论坛|算法论坛|ACM/ICPC论坛”

#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <assert.h>
#include <algorithm>
using namespace std;
 
#define MAX_NUM 21
int W, H;
char aTile[MAX_NUM][MAX_NUM];
 
int dp(int i, int j);
 
int cnt = 0;
int main()
{
	while(scanf("%d %d", &amp;W, &amp;H) == 2 &amp;&amp; W != 0 &amp;&amp; H 
 
!= 0)
	{
		memset(aTile, 0, sizeof(aTile));
		for(i = 0; i &lt; H; i++)
		{
			for(j = 0; j &lt; W; j++)
			{
				scanf("%c", &amp;aTile[i][j]);	
 
				if(aTile[i][j] == '@')
				{
					pos_i = i;
					pos_j = j;
				}
 
			}
		}
 
		printf("%d\n", dp(pos_i, pos_j));
	}
	return 0;
}
 
int dp(int i, int j)
{
	if(i&lt;0 || i&gt;=H || j&lt;0 || j&gt;=W)
		return 0;
	if(aTile[i][j] == '#')
		return 0;
	else
	{
		aTile[i][j] = '#';
		return 1 + dp(i+1, j) + dp(i-1, j) + dp(i, 
 
j+1) + dp(i, j-1);
	}

大家可以测试下:
输入:
6 9
….#.
…..#
……
……
……
……
……
#@…#
.#..#.
0 0
而输出却不是上面的图案。
这是为什么呢?原来:
scanf(“%c”,…)对于空格和回车键都可以接受(回车键包含回车

和换行2个ASCII码,分别是10和13,但是用scanf(“%c”)输入时再

输出则是10,这是因为%c只接受一个字符的原因),所以,解决方

法就是加一个getchar(),
代码局部如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 		for(i = 0; i < H; i++)
		{
			for(j = 0; j < W; j++)
			{
				scanf("%c", &aTile[i][j]);	
 
 
				if(aTile[i][j] == '@')
				{
					pos_i = i;
					pos_j = j;
				}
 
			}
			getchar();  //消除每输入6个字符的
 
换行的回车键
 
		}

但是会发现问题还是没有解决,这是为什么呢?
再观察,原来在while(scanf())中的回车键也没消除掉。
所以局部改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 	while(scanf("%d %d", &W, &H) == 2 && W != 0 && H 
 
!= 0)
	{
		memset(aTile, 0, sizeof(aTile));
		int i, j;
		int pos_i, pos_j;  // 为"@"的坐标
		getchar();         //消除while中scanf的回
 
车键
		for(i = 0; i < H; i++)
		{
			for(j = 0; j < W; j++)
			{

这下应该是对的了吧。
用freopen充定向去测试,把网页上题目给出的数据粘贴到txt中,

测试。。。还是乱,答案错误。
但是,用手动的去测试却发现,居然是对的。这下郁闷了,以为是

在CMD和TXT中的回车键的ASCII不一样,但是
测试都输出10。
终于,发现了是哪错误,原来在TXT中没一行数据末都多出以一个

空格!这就是元凶!记得在以前一道百练的题目
也是手动就是对的,但是freopen就错误了,先开始还以为是

freopen不能这么用,现在看来,十有八九是
同样的原因,下次在把数据从网页粘贴到TXT中要注意了,因为以

前可能对于%d没感觉。但是对于%c就不好说了,
所以在使用scanf(“%c”)是要格外注意空格和换行。

正题的代码如下(已AC):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 #include <iostream>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <assert.h>
#include <algorithm>
using namespace std;
 
#define MAX_NUM 21
int W, H;
char aTile[MAX_NUM][MAX_NUM];
 
int dp(int i, int j);
 
int cnt = 0;
int main()
{	
	//freopen("input_2816.txt", "r", stdin);
	while(scanf("%d %d", &W, &H) == 2 && W != 0 && H 
 
!= 0)
	{
		memset(aTile, 0, sizeof(aTile));
		int i, j;
		int pos_i, pos_j;  // 为"@"的坐标
		getchar();
		for(i = 0; i < H; i++)
		{
			for(j = 0; j < W; j++)
			{
				scanf("%c", &aTile[i][j]);	
 
 
				if(aTile[i][j] == '@')
				{
					pos_i = i;
					pos_j = j;
				}
 
			}
			getchar();  //消除没输入6个的换行
 
的回车键
 
		}			
		printf("%d\n", dp(pos_i, pos_j));
	}
	return 0;
}
 
int dp(int i, int j)
{
	if(i<0 || i>=H || j<0 || j>=W)
		return 0;
	if(aTile[i][j] == '#')
		return 0;
	else
	{
		aTile[i][j] = '#';
		return 1 + dp(i+1, j) + dp(i-1, j) + dp(i, 
 
j+1) + dp(i, j-1);
	}
}

但是,针对有回车和空格的字符输入来说,gets()是一个好的选择


(gets()版的解法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 #include <iostream>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <assert.h>
#include <algorithm>
using namespace std;
#define MAX_NUM 21
int W, H;
char aTile[MAX_NUM][MAX_NUM];
 
int dp(int i, int j);
 
int cnt = 0;
int main()
{
	int i, j;
	//freopen("input_2816.txt", "r", stdin);
	while(scanf("%d %d", &W, &H) == 2 && W != 0 && H 
 
!= 0)
	{
		for(i = 0; i < H; i++)
			scanf("%s", aTile[i]);
		for(i = 0; i < H; i++)
			for(j = 0; j < W; j++)
				if(aTile[i][j] == '@')
					printf("%d\n", dp
 
(i, j));
	}
	return 0;
}
 
int dp(int i, int j)
{
	if(i<0 || i>=H || j<0 || j>=W)
		return 0;
	if(aTile[i][j] == '#')
		return 0;
	else
	{
		aTile[i][j] = '#';
		return 1 + dp(i+1, j) + dp(i-1, j) + dp(i, 
 
j+1) + dp(i, j-1);
	}
}

发布者

Tanky Woo

Tanky Woo,[个人主页:https://tankywoo.com] / [新博客:https://blog.tankywoo.com]

《scanf输入字符的总结–百练2816红与黑》有20个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注