题目1069:查找学生信息

这篇文章中提到的问题主要是由于调试平台Visual Studio和测试平台Online Judge的一些小差异,造成在Visual Studio中调试通过的代码,在输入OJ时要手动修改函数,后知后觉,可以设置一下编译参数来让Visual Studio尽量使用标准C++库中的函数。这样解决已经能够极大缓解这个矛盾,但是由于最新的Visual Studio 2015的缘故,有些函数完全不被支持了,例如gets()(后来发觉,这个函数似乎确实不那么好用),这时,会有静态编译错误提示,在Visual Studio中按下F1键进入微软的官方文档查询相关函数的使用说明是最快的解决办法,百度谷歌此时并不会比MSDN更有帮助,当然,更不要像我一样怀疑自己:忘记了这么多?

要看某个函数在标准库中什么位置,这里是个好去处

遇到这个问题的,估计都是和我一样的入门级选手,不要怀疑自己,一个逻辑,只有自己实现过、调试过,才能真正理解;一个逻辑,只有多犯几次错误并一次次修正,才能真正掌握。

最初的想法

typedef struct {
int no; //【错误】学号是整型最好,但这是你简化问题的想法,看样例中学号似乎不是整型
char name[10];
char gender[2];
int age;
} info; info data[1000];
int query[10000]; int biSearch(info data[], int length, int queryNo); int main()
{
int n, m; scanf_s("%d", &n);
for (int i = 0; i < n; i++) //【错误】过度简化问题,样例中的学号是升序的,不代表所有样例都是升序输入的。应该考虑将输入的数据排序。
{ //【错误】如果升序输入了学号,就不要查找了,直接放入info[]中,直接使用下标就可以访问相应元素。
scanf_s("%d %s %s %d", &data[i].no, &data[i].name, &data[i].gender, &data[i].age);
} scanf_s("%d", &m);
for (int i = 0; i < m; i++)
{
scanf_s("%d", &query[i]);
}
return 0;
} int biSearch(info data[], int length, int queryNo)
{
int start = 0;
int end = length - 1;
int middle = (start + end) / 2; while (start < end)
{
if (middle < queryNo) //【错误】根据上面分析,学号为字符串,这里的比较就是错误的,于是改为if (middle < (int)queryNo),仍然【错误】
{
start = middle;
}
if (middle > queryNo)
{
end = middle;
}
if (middle == queryNo)
{
return middle; //【错误】写到这里感觉有些奇怪,这里的几个分支似乎是没有意义的,因为做了一个过度简化:输入的学号是升序的,见上面注释。
}
} }

调试中遇到的问题

  • scanf_s的使用

    • 在Visual Studio中,为加强安全性,使用scanf_s代替scanf,更安全的版本在输入字符串时,有讲究。
    • 若按照scanf_s("%s%s", buf1, buf2);使用,会提示错误:C4477,“scanf_s”: 格式字符串“%s”需要类型“int”的参数,但可变参数 2 拥有了类型“char *”
    • 原因是,没有为存储字符串的变量指定其长度。
    • 指定了格式%s后,可变参数中对应一个字符串指针char buf[10]后面跟一个存储该字符串的变量的长度,可以使用_countof(buf)来获取该变量的长度。
    • 这样就要对字符串的长度做仔细的考虑,比如这题中有个字符串存放性别,char gender[2]会引起错误,因为汉语的男或女占用2B,这里的长度刚刚够存放字符串而没有空间存放结束符\0,于是出现错误。char gender[5]就可以正确保存。
  • 从文件中读取fscanf_s()
    • 调试过程肯定要多次启动程序,不想每次都输入非常长的测试用例,于是想把测试用例存入文件,将文件注入标准输入流。
    • 因此尝试了在Visual Studio中使用fscanf_s,由于安全原因,在Visual Studio中使用fscanf与其他环境中也不太一样。参数类型不一样。
    • 【TODO】需要好好读一下MSDN,了解这个用法。
FILE* fp;
errno_t err;
err = fopen_s(&fp, "student", "r"); //在标准C++中,第一个参数直接使用FILE*即可,这里要使用FILE** if (err == 0) //fopen_s返回0则说明读取文件正确
{
...
}
else
{
printf_s("无法读取文件");
}
  • 在控制台直接粘贴

    • 后来发现,与上面从文件读取相比,还是直接在控制台右键粘贴测试用例更方便。
    • 唯一遇到的小问题是,粘贴时,最后一行没有换行符,调试时到了这里就不动了,也没有错误提示。
    • 是什么问题卡住了?很简单——到了最后一组测试数据的最后一行时,按下Enter键将换行符补充上即可!
  • scanfscanf_s
    • 在VS中总提示scanf不安全,代码调试好后在OJ上验证,又要把所有的scanf_s换成scanf
    • 所谓的不安全,是scanf不会对字符串的长度做检查,有可能一直从控制台的缓存中读数据而超出字符串的长度限制,自己注意这一点就是安全的。
    • 避免这种繁琐的步骤,在VS的编译选项中添加\D _CRT_SECURE_NO_WARNINGS ,“右击工程-属性-配置属性-C/C++-命令行”,从而在VS中也使用标准的函数scanf
  • Visual Studio中一个奇怪的错误
    • stdio.h中出现了一个错误???!!!错误代码是_CRT_END_C_HEADER,错误提示是此类型没有存储类或类型说明符,这是什么问题???
  • OJ上遇到的问题
    • 总提示错误,添加空格,修改输出方式,都没用。
    • 结果原因是,存放学号的字符数组太小了!char no[50]不够,修改为cha no[100]就通过了。
    • 测试样例会很大,不要太小气,内存要求很宽裕时,尽量使用大数组!

编程实践中需要改进的地方

  • 小心使用强制类型转换

    • char queryNo[50]对这个字符串强制转换为整型,会出现什么结果?将得到一个非常大的整数,因为这会把50B的长度截取4B然后转换???
    • 因此if (middle < (int)queryNo)将整型的middle和这个转换来的未知的整数比较大小,会得到错误的结果。
    • 更好的做法是,是使用strcmp(),将字符串形式的学号直接使用该函数比较。
  • 字符串数组
    • 有一个问题,每读入一个查询学号,就输出一个学生的信息。样例中是把所有的学生信息放在一起输出。(后来发现,无需这样做!)
    • 因此,想将待查询的学号存入一个字符串数组,这里出现较大问题。字符串一直都没有掌握好,总出现内存越界的问题。
    • _countof()是计算一个数组的元素个数的,如果是二维数组,将返回行数。若希望得到二维数组的列数(即字符串长度),就引入sizeof(),二者配合得出。
    • scanf_s("%s", ..., sizeof(array) / _countof(array))中指定字符串长度的参数的正好就是字符串数组中每个字符串的长度。
char query[10000][50];
scanf_s("%s", query[i], 50);
/*_countof(query));起初想用VS中的宏来获得字符串的长度
*这里使用`_countof(query)`无法计算出字符串数组的单个元素的长度,会出现编译错误,因此采用硬编码,将50直接作为`scanf_s`的参数。
*后来自己测试了一下宏`_countof()`究竟是什么含义:_countof()计算一个数组的元素的个数
*/ #include <stdio.h>
#include <stdlib.h> //_countof()在这里定义 int main()
{
char array[10][30];
printf_s("%d", sizeof(array) / _countof(array));
return 0;
}

完整代码

// 机试指南-例2.10.cpp : 二分查找。
// #include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <algorithm> using namespace std; struct info {
char no[50];
char name[20];
char gender[5];
int age; bool operator < (const info & A) const
{
return strcmp(no, A.no) < 0; //【学习】使用strcmp
}
} buf[1000]; char query[10000][50];
int biSearch(int length, char queryNo[]); int main()
{
int n, m; scanf_s("%d", &n);
for (int i = 0; i < n; i++)
{
scanf_s("%s%s%s%d", buf[i].no, _countof(buf[i].no),
buf[i].name, _countof(buf[i].name),
buf[i].gender, _countof(buf[i].gender),
&buf[i].age);
} //【错误】VS中scanf输入字符串与标准C++不太一样,多一个参数来指定字符串的长度 sort(buf, buf + n); scanf_s("%d", &m);
//输入要查询的学号
for (int i = 0; i < m; i++)
{
scanf_s("%s", query[i], 50);//, _countof(query)); //【疑问】保存字符串数组,如何指定存入何处???不是地址的问题,是scanf_s需要字符串的长度
} for (int j = 0; j < m; j++)
{
int rank = biSearch(n, query[j]);
if (rank == -1)
{
printf_s("No Answer!\n");
}
else
{
printf_s("%s %s %s %d\n", buf[rank].no, buf[rank].name, buf[rank].gender, buf[rank].age);
}
}
/*
*while (m-- != 0) //每输入一个学号,二分查找一次;得到的输出与样例不符
*{
* char query[50];
* //scanf_s("%s", query, _countof(query));
* int rank = biSearch(n, query);
* if (rank == -1)
* {
* printf_s("No Answer!\n");
* }
* else
* {
* printf_s("%s %s %s %d\n", buf[rank].no, buf[rank].name, buf[rank].gender, buf[rank].age);
* }
*}
*/
return 0;
} //二分查找,返回找到的目标所在的下标
int biSearch(int length, char queryNo[])
{
int start = 0;
int end = length - 1;
int middle = (start + end) / 2; while (start <= end) //【错误】这里必须有等号!
{
if (strcmp(buf[middle].no, queryNo) < 0) //【错误】使用库函数strcmp。起初二分的逻辑搞错了。
{
start = middle + 1;
middle = (start + end) / 2;
}
else if (strcmp(buf[middle].no, queryNo) > 0)
{
end = middle - 1;
middle = (start + end) / 2;
}
else
{
return middle;
}
} return -1;
}

【九度OJ】题目1096-二分查找的更多相关文章

  1. 九度oj 题目1173:查找

    题目描述: 输入数组长度 n 输入数组      a[1...n] 输入查找个数m 输入查找数字b[1...m]  输出 YES or NO  查找有则YES 否则NO . 输入: 输入有多组数据. ...

  2. 九度oj 题目1177:查找

    题目描述: 读入一组字符串(待操作的),再读入一个int n记录记下来有几条命令,总共有2中命令:1.翻转  从下标为i的字符开始到i+len-1之间的字符串倒序:2.替换  命中如果第一位为1,用命 ...

  3. 九度oj 题目1096:日期差值

    题目描述: 有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天 输入: 有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD 输出: 每组数据输出一行, ...

  4. 九度OJ 题目1384:二维数组中的查找

    /********************************* * 日期:2013-10-11 * 作者:SJF0115 * 题号: 九度OJ 题目1384:二维数组中的查找 * 来源:http ...

  5. hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人

    钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  6. 九度oj题目&amp;吉大考研11年机试题全解

    九度oj题目(吉大考研11年机试题全解) 吉大考研机试2011年题目: 题目一(jobdu1105:字符串的反码).    http://ac.jobdu.com/problem.php?pid=11 ...

  7. 九度oj 题目1007:奥运排序问题

    九度oj 题目1007:奥运排序问题   恢复 题目描述: 按要求,给国家进行排名. 输入:                        有多组数据. 第一行给出国家数N,要求排名的国家数M,国家号 ...

  8. 九度oj 题目1087:约数的个数

    题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...

  9. 九度OJ题目1105:字符串的反码

    tips:scanf,cin输入字符串遇到空格就停止,所以想输入一行字符并保留最后的"\0"还是用gets()函数比较好,九度OJ真操蛋,true?没有这个关键字,还是用1吧,还是 ...

  10. 九度oj题目1009:二叉搜索树

    题目描述: 判断两序列是否为同一二叉搜索树序列 输入:                        开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束. 接 ...

随机推荐

  1. PKUSC 模拟赛 day1 上午总结

    思考了一下第二题,觉得有无数种乱搞做法 类似什么bitset压位,MCS染色之类奇怪的做法 然而都是玄学正确性或者玄学复杂度 先放题解把 第一题显然具有单调性,二分就可以啦 O(nlogn),貌似输出 ...

  2. 欧拉工程第72题:Counting fractions

    题目链接:https://projecteuler.net/problem=72 真分数;n/d 当d ≤ 1,000,000时候的真分数有多少个 public class P72{ void run ...

  3. git Unstaged changes after reset

    转载:http://my.oschina.net/yuzn/blog/150275 相信大家都做过这个操作,就是本地做了修改后,不想提交,想恢复如初 git reset HEAD   这样的话,我们就 ...

  4. python 类成员函数

    http://cowboy.1988.blog.163.com/blog/static/75105798201091141521583/ 这篇文章总结的非常好 主要注意的地方是 1,在类内调用成员函数 ...

  5. Android权限安全(13)4.3前后root原理不同

    在JB MR2(4.3)之前 Apk内部可以通过Java的Runtime执行一个具有Root-setUID的可执行文件而 提升Effective UID来完成一些特权操作,典型的Root包中的su就是 ...

  6. BZOJ 3132 上帝造题的七分钟(二维树状数组)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=3132 题意:给出一个矩阵,两种操作:(1)将某个子矩阵的数字统一加上某个值:(2)查询某 ...

  7. unity3d5.2.3中 调整视角

    按住alt键不放,然后左边的手的图标会变成一个眼睛,在Scene中移动.就会发现可以调整视角了

  8. hadoop2的automatic HA+Federation+Yarn配置的教程

    前言 hadoop是分布式系统,运行在linux之上,配置起来相对复杂.对于hadoop1,很多同学就因为不能搭建正确的运行环境,导致学习兴趣锐减.不过,我有免费的学习视频下载,请点击这里. hado ...

  9. hdu 4972 A simple dynamic programming problem (转化 乱搞 思维题) 2014多校10

    题目链接 题意:给定一个数组记录两队之间分差,只记分差,不记谁高谁低,问最终有多少种比分的可能性 分析: 类似cf的题目,比赛的时候都没想出来,简直笨到极点..... 最后的差确定,只需要计算和的种类 ...

  10. BZOJ_1029_[JSOI2007]_建筑抢修_(贪心+优先队列)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1029 \(n\)个任务需要完成,给出每个任务所需时间\(t_1\)与deadline\(t_2 ...