学员周报展示(10)

发布者:管理员发布时间:2018-12-24浏览次数:172

13周周报

解孝甜

周一:做校赛第二题

问题描述

Excel单元格的地址表示很有趣,它使用字母来表示列号。

比如,

A表示第1列,

B表示第2列,

Z表示第26列,

AA表示第27列,

AB表示第28列,

BA表示第53列,

....

当然Excel的最大列号是有限度的,所以转换起来不难。

如果我们想把这种表示法一般化,可以把很大的数字转换为很长的字母序列呢?

本题目即是要求对输入的数字,输出其对应的Excel地址表示方式。

样例输入

26

样例输出

Z

样例输入

2054

样例输出

BZZ

数据规模和约定

我们约定,输入的整数范围[1,2147483647]



思路:要求是把一个Excel中数字表示方式转换为字母表示,首先要输入一个数字n,再把n转化为字母输出,字母是二十六进制,但是由于字母表示数字是从1开始的,即1A表示,26Z表示,满271,即27表示为AA,以此类推。用数学表达式就是n表示被除数,26为除数,当余数为0时就从商里面取1,这时余数变为26,商减1,继续进行,直到商为0时为止,然后1-26对应A-Z。本质上就是辗转相除法,只是进制变了。而要怎么把数字转 换成字母呢?这类似我们之前做过的一道题,当时那道题是把字母转化为数字,当时A代表10是用a=n-’A’+10表示的,这里就可以类比,区别在于这里的输出格式是字符型不是整型,就要进一步转化,在原有的输出基础上在前面加上一个char,即可以输出字符型。


收获:这个题目是学长几周前讲过的,当时只是听了思路还没有来得及去做,就放着去做其他的了,过了这么久对当时学长讲的只有模糊的记忆了,开始自己想了好久都没有理清楚思路不知怎么下手,后来又问了学长,听他给我讲了一遍再回过头来写,有了初步的思路,其中遇到了几个问题:①当完全除尽时怎么实现余数变为26,商减1; 解决办法:在for循环中加一个if语句当余数为0时再次改变商和余数的值。

②for语句的结束条件是什么; 解决办法:当商为0时跳出循环即用break语句。

如何存储余数(余数就是我们需要转换成字母的部分)解决办法,用一个数组存储。

如何输出转化后的字符串;解决办法:由于余数就是与每个位的字母对应的数字,而余数是从低位到高位存储的,故输出时应该倒序输出,即还要用到一个for循环倒序输出存储余数的这个数组中的值。

开始我写程序时由于存储余数的数组为m[i],于是最后一个for循环输出数组时我也写成了m[i],而我为了和上面区分这里用的计数器是j,本来只要数组名是m不变的话对于写m[i]还是m[j]是没有关系的,可是我这里对j的限制条件是j<=i,即用到了i前面得到的值,在这里i的值就固定了,输出的m[i]就是一个数,而不是i个数了。(这个错误和我上周排序时犯的是同一个错)

最后就是字符形式输出问题了,因为我定义的数组是整型,而要求的输出字母,就要在输出的m[i]前面加一个char。(请教别人得知的)


后来我又想能不能不在输出的时候加char,直接在前面把m[i]定义成字符型,直接输出m[i]应该会更简单,的确可以。

周二周三做数据

周四周五学习,做校赛第5题。


代码:#include<iostream>

 using namespace std;

 int vis[10000]={0},sum=0,n;

 int main()

 {

 int fun(int k);  

 int k=1;

 cin>>n;

 fun(k);

 cout<<sum;

 return 0;

 }  

 int fun(int k)

 {

 int i;

 if(k==n+1)

 {

 sum++;

 return 0;

 }

 if(k==1)

  {

 for(i=k;i<=k+1;i++)

 {

 if(vis[i]==0)

 {

 vis[i]=1;

 fun(k+1);

 vis[i]=0;

     }

  }

  }

 else

 if(k>1&&k<n)

 {

 for(i=k-1;i<=k+1;i++)

 {

 if(vis[i]==0)

 {

 vis[i]=1;

 fun(k+1);

 vis[i]=0;

     }

 }

 }

 else

 {

 for(i=k-1;i<=k;i++)

 {

 if(vis[i]==0)

 {

 vis[i]=1;

 fun(k+1);

 vis[i]=0;

     }

 }

 }

 }


运行结果图:

基本思路:用递归调用一个funk函数,每个位置除了第一个和最后一个位置只能有两种选择,中间的位置有三种选择,每次先排好第一个数再考虑后面的数,首先把所有供选择的数初始化为0,如果这个数用过了就标记为1,然后递归调用funk函数,后面选择时只能从标记为0的里面选择,直到第n+1个位置时sum++,并返回上一层,返回上一层时原先这一层用过的数字就又重新标记为0,这样依次进行,只要返回上一层原先该层用过的数字就变为0,这样依次进行,最后输出sum的值。


收获:1.递归时定义函数为以本题为例,写作int funkint k)后面没有分号,声明写作int funkint k);要加分号,而调用时写作funk(k);没有int

  1. 递归调用时里面的数组不能太大,因为函数调用会循环多次,这样函数运行时可能就会出错。

  2. 要给k赋初值为1,不然k的值就得不到你的预期结果,因为如果不赋初值的话k的值就是未知的,计算机会随机赋初值就不能按照我们预期的步骤一步步进行下去了。

  3. 我一开始只是把三个if条件写出来了,把当k=n+1时,sum++写到了主函数里面,而funk函数里面没有,这样就不能使程序终止,后来问了学长才知道是没有终止条件,这里k=n+1就是终止条件,所以应该包含着funk函数里面。

  4. 还有一点是学长之前说了当k=n+1时一轮结束,要返回上一层,此时该层用过的i就要重新初始化VIS[i]=0,我开始不知道不知道这个语句应该放在那里,根本原因是我一开始没有搞清楚程序具体执行的顺序,当递归调用funkk+1)时函数就会执行k=k+1时的语句,以此类推,故初始化应该在最后一位k=n+1后面初始化,然后就会跳到上一层,故应该在每层调用funk函数后初始化VIS[i]=0。(看了学长发的视频知道这种方法叫回溯)

  5. 最后一点就是如何确定某个数字i是否在前面已经用过了,这就要用一个判断语句,由于我们把每个用过的数字都标记vis[i]=1,故只要判断vis[i]是否为0就可以知道用没用过了,如果没有用过就可以调用funk函数,反之不行,再者前面有满足某个条件就标记为1,如果在这之后vis[i]=1是肯定的,再判断也是没有必要的,所以应该放在调用funk函数之前,并且在标记为vis[i]=1之前。

  6. 最后就是return 0的位置一开始我是放在最后面的,根据递归的运行顺序是不能到达这样return 0的,如果不加可能会溢出,为了防止这种现象就把return 0加在sum++的后面就可以了。

  7. 如果主函数和被调用的函数都用到了某个变量则为了避免重复定义可以直接定义为全局变量。

  8. 这里也可以把每个位置的值i用一个数组a[k]存起来,但我觉得不存也不影响结果。