数组在处理枚举无规律数据中的应用

2019-04-19 01:35佘可
新课程研究·教师教育 2019年2期
关键词:数组字典

摘 要:计算机程序设计高级语言在处理数据的时候一般要用到循环,强调数据的规律性,文章指出在规律不明显的情况下可以用数组做字典,用查表的方法使用数据,让枚举或者统计程序大大简化。

关键词:枚举;数组;字典

作者简介:佘可,湖北省咸宁高中。(湖北 咸宁 437000)

中图分类号:G633.67 文献标识码:A 文章编号:1671-0568(2019)06-00102-03

现代计算机的主要应用之一是进行数据处理,一般的高级程序设计语言如Pascal,C,C++,VB,Python等都有顺序、选择、循环三种基本程序结构和整数、实数、字符串、数组等基本数据类型。其中,顺序结构让程序按流程自动执行,选择结构主要用于做出判断进行分支,循环结构主要用于按规律自动执行某些操作,而整数、实数等数据类型可以描述基本数据,数组可以进行简易的大数据组织。这些有机结合在一起的就是简单程序设计的基础,可以处理一般的数据计算。当前,程序设计者的主要目的是实现程序的易读性和可扩展性,所以程序中如何找到更普适的规律显得非常重要,数组除了可以方便组织大规模的数据之外,还可以巧妙地进行规则的简化。

比如,一次学生考试中,卷面满分100分,实际得分用ABCDE五个等级表示,卷面得分90分以上(含)换算成A,80分以上(含)换算成B,70分以上(含)换算成C,60分以上(含)换算成D,低于60分的为E,这是一个典型的分支的例子,一般的教材上采用if嵌套或者多分支switch来实现,典型如下:

cin>>x;

if (x>=90) cout<<'A';

else if (x>=80) cout<<'B';

else if (x>=70) cout<<'C';

else if (x>=60) cout<<'D';

else cout<<'E';

此程序简单易读,但是要修改和扩展就显得不便,假设分级的分数不是 90,80,等级不是5个而是更多,那么程序的修改会较大,我们尝试使用高级语言里面的数组来找到更好的对应关系。

高级语言一般都有数组这种基本的数据类型,如C++定义数组 int a[10],这样建立了数字a[i]和下标i之间的对应关系,可以通过下标i访问数据a[i],规律不明显甚至没有规律的数据。设置两个数组分别对应分级的分数和等级,从小到大枚举到小于自己分数的最大分级分数,对应的等级就是该得的等级。

#include

using namespace std;

const int s[6]= {0,60,79,80,90,9999};

const char g[6]= {'E','D','C','B','A','O'};

//基本等价 cosnt string s="EDCBA";

int i,x;

int main()

{

cin>>x;

{

for (i=0; (s[i]<=x); i++); //找到第一个大于x的分数值

i--; //回退一格

cout<

}

return 0;

}

因此,巧妙利用数组建立一个分数和等级之间的一个对应表,用查表的方法来做数据分析,可以使程序更加简洁,使扩展性大大提高,也不损失效率和可读性。下面再看两个例子:阶梯电费(洛谷P1010),据闽价电[2006]27号规定,月用电量在150千瓦时及以下部分按每千瓦时0.4463元执行,月用电量在151~400千瓦时的部分按每千瓦时0.4663元执行,月用电量在401千瓦时及以上部分按每千瓦时0.5663元执行。请编写一个程序,已知用电总计,根据电价规定,计算出应交的电费应该是多少。

分级电价的起点和电费之间没有什么明显规律,仿照上面的例子,用m数组记录电价分级起点,对应的电费就放在r数组里面,枚举i,直到m[i]>x,然后往下统计电费。

#include

using namespace std;

int s[] = {0,150, 400, 9999};

double f[] = {0,0.4463, 0.4663, 0.5663};

int x,i;

float p;

int main()

{

cin>>x;p=0;

for(i = 0; s[i] < x; i++) ; //找到最大的小于x的階梯位置

s[i]=x; //取代这个数字

for (; i>0; i--) p+=f[i]*(s[i]-s[i-1]); //反向阶梯计价累加

cout<

}

进制转换(经典例题),以十进制转换成十六进制为例,一般使用短除法。将x除以16,余数进行处理,商继续除。但是余数小于10的时候对应显示0123456789,余数大于10的时候要用ABCDEF六个字符分别表示10-15,一般的教科书这段是这样写的,用一个判断对余数进行分别处理:

while (x>0)

{

k=x%16;//取得余数

if (k<10)s=char(k+'0')+s; //小于10的余数按数字处理

else s=char(k-10+'A')+s;// >=10的余數按字母处理

x=x/16;

}

利用若用数组建立0-15与字符‘0-‘F的对应关系,先定义const string tzb=”0123456789ABCDEF”,那么数字和字符之间就建立了连续的对应关系,程序长度大大减少:

while (x>0)

{

k=x%16;//取得余数

s=tzb[k]+s; //将余数对应的字符加在s前面

x=x/16;

}

判断今天星期几。有一种判断星期几的思路是:已知公元1900年1月1日星期日,看今天到那天共有多少天,这个日期除以7的余数就是星期几。但是这里有几个需要分支计算的地方,一个是每个月的天数根据月份的不同而不同,输出的0-6也要换算成人们容易接受的Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,这里我们都按上面的做法用数组处理。定义整数数组int month[]={0,31,28,31,30,31,30,31,31,30.31,30,31} 分别表示每个月的天数,这样直接查表就可以省掉很多月份的判断;再定义字符串数组 string s[]={“Sunday”,”Monday”,”Tuesday”,”Wednesday",”Thursday”,”Friday”,”Saturday”},这样得到余数后按下表输出对应的字符串就是相应的星期。

#include

using namespace std;

int y,m,d,i;

int month[]= {0,31,28,31,30,31,30,31,31,30,31,30,31};

string ss[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

long long s;

bool islip(int x)

{

bool b1=(x%400==0);

bool b2=(x%4==0)&&(x%100!=0);

return b1||b2;

}

int main()

{

cin>>y>>m>>d;s=0;

for (i=1900;i<=y-1;i++)

if (islip(i)) s+=366;else s+=365;

if (islip(y)) month[2]++;

for (i=1;i<=m-1;i++) s+=month[i];

s+=d;

s%=7;

cout<

return 0;

}

扫雷游戏(NOIP2015普及组真题)。在n行m列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。现在给出n行m列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。

输入格式:

第一行是用一个空格隔开的两个整数n和m,分别表示雷区的行数和列数。接下来n行,每行m个字符,描述了雷区中的地雷分布情况。字符“*”表示相应格子是地雷格,字符“?”表示相应格子是非地雷格。相邻字符之间无分隔符。

输出格式:输出文件包含nn行,每行mm个字符,描述整个雷区。用“*”表示地雷格,用周围的地雷个数表示非地雷格。相邻字符之间无分隔符。

输入样例1:

3 3

*??

???

?*?

输出样例1:

*10

221

1*1

输入样例2:

2 3

?*?

*??

输出样例2:

2*1

*21

每个格子要统计其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子的信息,虽然用坐标可以用-1,0,1两两组合,但是没有明显的规律,要是写8个if判断的话会比较麻烦,可以考虑使用数组来对应这些坐标偏移形成规则。

设置行列偏移数组dr[]={-1,-1,-1,+0,+0,+1,+1,+1}; dc[]={-1,+0,+1,-1,+1,-1,+0,+1};这样当i=0,1,2,3,4,5,6,7的时候,dr和dc的取值就分别是(-1,-1),(-1,0),(-1,1)等8个方向的增量,完成了规则的对应。

#include

using namespace std;

const int mm=110;

int m,n;

char a[mm][mm];

int r,c,s;

int dr[]={-1,-1,-1,+0,+0,+1,+1,+1}; //8个方向的行r增量

int dc[]={-1,+0,+1,-1,+1,-1,+0,+1}; //8个方向的列c增量

int tr,tc,i;

int main()

{

cin>>m>>n;

for (r=1; r<=m; r++)

for (c=1; c<=n; c++) cin>>a[r][c]; //度的原始棋盘

for (r=1; r<=m; r++)

{

for (c=1; c<=n; c++)

if (a[r][c]=='*') cout<

else

{

s=0;

for (int i=0;i<=7;i++)

{

tr=r+dr[i]; tc=c+dc[i]; //计算行列值

if (a[tr][tc]=='*') s++; //判断累加

}

cout<

}

cout<

}

return 0;

}

由上面的例子看出:合理设置一些数组常量,形成类似字典的形式,可以将一些本来规律性不明显的数据变成可以枚举的数组下标,再利用循环减少程序中代码长度,增加扩展性。一些典型的处理,如ISBN编码,模拟扑克发牌,螺旋方阵等数据处理中都存在这样的应用。

参考文献:

[1] 董永建.信息学奥赛一本通[M].北京:科学技术文出版社,2013.

责任编辑 张庆晓

猜你喜欢
数组字典
JAVA稀疏矩阵算法
JAVA玩转数学之二维数组排序
2020,我想把“错过”从字典拿掉
深入浅出理解C语言指针与二维数组
字典的由来
大头熊的字典
更高效用好 Excel的数组公式
倍增法之后缀数组解决重复子串的问题
正版字典
寻找勾股数组的历程