二、阅读程序
1.
#include <cstdlib> #include <iostream> using namespace std; char encoder[26] = {'C','S','P',0}; char decoder[26]; string st; int main() { int k = 0; for (int i = 0; i < 26; ++i) if (encoder[i] != 0) ++k; for (char x ='A'; x <= 'Z'; ++x) { bool flag = true; for (int i = 0; i < 26; ++i) if (encoder[i] ==x) { flag = false; break; } if (flag) { encoder[k]= x; ++k; } } for (int i = 0; i < 26; ++i) decoder[encoder[i]- 'A'] = i + 'A'; cin >> st; for (int i = 0; i < st.length( ); ++i) st[i] = decoder[st[i] -'A']; cout << st; return 0; }
【分析】此题最直接的方法就是,首先根据代码逻辑将decoder和encoder两个数组用表格画出来,不要节省草稿纸,完整的画出来,你会看得更清楚,且不容易出错。
encoder数组就是A-Z字母序列,将C,S,P这3个字母移到了最前面,然后形成的字母列表,所以原先排在C,S,P这3个字母前面的字母统统后移,S后面的字母从T-Z这7个字母位置不变。
decoder数组,将encoder在下标i上的字母的在原字母表的位置上放置原字母表的第i个字母。
对于输入字符串st中的字母,根据表中的第一行查找对应decoder表对应的字母就是输出的字母。
- 正确
显然输入的字符串st只能有大写字母组成,因为encoder和decoder数组长度都只有26,第30行代码st[i] = decoder[st[i] -'A'];
如果st[i]不是大写字母,则st[i] -'A'的值就会超过0-25的范围,导致此处数组越界 - 错误
显然若输入字母是T-Z字母组成,则输入和输出相同 - 正确
因为第12行统计encoder数组中的非零字符,只有3个,只要不小于3就可以 - 错误
第26行是要遍历整个encoder数组,生成decoder数组,必须要完整遍历26次
5和6直接根据表格即可得出,表格第一行是输入字母,最后一行是对应的输出字母。
2.
#include <iostream> using namespace std; long long n, ans; int k, len; long long d[1000000]; int main() { cin >> n >> k; d[0] = 0; len= 1; ans = 0; for (long long i = 0; i <n; ++i) { ++d[0]; for (int j = 0; j + 1<len; ++j) { if (d[j] == k) { d[j] = 0; d[j + 1] += 1; ++ans; } } if (d[len- 1] == k) { d[len - 1] = 0; d[len] =1; ++len; ++ans; } } cout << ans << endl; return 0; }
【分析】仔细观察13行代码开始的for循环,循环n次,每次循环都给d[0]加1,然后继续看后面的代码,首先是15-21行的内层的for循环,遍历下标j从0~len-2,依次检测d[j]看其是否达到k,若达到k,则将d[j]置0,然后d[j+1]加1,同时ans加1。然后是22-27行,是检测d[len-1]是否达到k,若达到k,则将d[len-1]置0,然后d[len]置1,同时len加1,ans加1。
可以推测出,d数组最终的结果是十进制数n的k进制的表示,从左至右,是低位到高位,从0开始,d数组初值均为0,每次在最低位d[0]加1,循环过程整记录低位向高位的进位次数,len表示当前转换的k进制数的位数。
所以15-21行是处理低位的进位,22-27行是处理最高位的进位,最高位若进位,则总的数位数len加1。
- 错误
举反例,当k=1时,若n=1,则输出ans时,len=2 - 错误
举反例,当k>1时,若n=1,则输出ans时,len=1 - 正确
k>1时,len是n转为k进制数的总位数,显然len位k进制数的最大值为,所以n最大为
- D
k=1是特殊的一种情况,这种情况下,跟踪代码可以发现len将始终为2,后面每次d[0]自增1,都会同时使d[1]自增1,ans的值就等于n - B
这里是在3进制下,将一个数从0自增到过程中产生的低位到高位进位的次数,最低位到最高位依次记为d[0],d[1],d[2],...d[30],最终结果依次是0, 0, 0, ..., 1,d[0]每自增3次就向d[1]进位,d[1]每自增3次就向d[2]进位,d[1]每自增3次等价于d[0]自增9次,依次类推,最后一次进位是d[29]向d[30]进位,是d[0]自增
次产生的进位,所以总的进位数需要计算全部的低位向高位的进位:
d[0]->d[1],有次
d[1]->d[2],有次
......
d[29]->d[30],有次
总进位次数ans= - D
在前面第5题的分析基础上,本题也迎刃而解,10进制下,不用转换,n本身就是10进制展示的,将100,010,002,000,090依次拆分为下表格左边所示的4个数,参照第5题,求出每个数的进位总数,累加即可得到结果。
3.
#include <algorithm> #include <iostream> using namespace std; int n; int d[50][2]; int ans; void dfs(int n, int sum) { if (n == 1) { ans = max(sum, ans); return; } for (int i = 1; i < n; ++i) { int a = d[i - 1][0], b = d[i - 1][1]; int x = d[i][0], y = d[i][1]; d[i - 1][0] = a + x; d[i - 1][1] = b + y; for (int j = i; j < n - 1; ++j) d[j][0] = d[j + 1][0], d[j][1] = d[j + 1][1]; int s = a + x + abs(b - y); dfs(n - 1, sum + s); for (int j = n - 1; j > i; --j) d[j][0] = d[j - 1][0], d[j][1] = d[j - 1][1]; d[i - 1][0] = a, d[i - 1][1] = b; d[i][0] = x, d[i][1] = y; } } int main() { cin >> n; for (int i = 0; i < n; ++i) cin >> d[i][0]; for (int i = 0; i < n;++i) cin >> d[i][1]; ans = 0; dfs(n, 0); cout << ans << endl; return 0; }
【分析】此题具有一定难度。首先拿到此题,一下子不容易读懂这题究竟是求的什么,但是有一个很明显的特征就是dfs这个函数,是很典型的DFS回溯算法,从回溯算法套路入手,回溯算法的套路模板大致如下:
void traceback(n, ...){ if(达到叶子节点){ 更新结果,返回 } //遍历n-1个子节点 for(i=1, i<n, i++){ 依次做选择,选择某个子节点 设置现场,可能有一些值将被临时修改 //递归调用 traceback(n-1, ...) 撤销本次选择,恢复现场,还原被修改的值 } }
搜索树结构大致入下图所示,比如n=4的时候。
dfs函数的第一个参数n,可以认为是搜索树的高度,根节点高度为n,叶子节点高度为1,dfs函数的第二个参数是当前搜索节点下的累积结果,每一条搜索路径搜索到n=1时到达叶子节点,得到一个当前最大的ans值,返回,然后逐层往上回溯,继续搜索其他路径,寻找更大的ans值,并更新ans值。
对照代码,15-21行是设置现场,也就是当前选择节点i之后,进行的一系列的计算,得到s值,然后追加到sum,进行下一层递归调用,递归返回后,23-26是撤销选择,恢复现场。
然后具体分析具体的代码逻辑,主要是15-21行,如下图所示:
每次递归执行到当前子节点的第i个节点时,将d数组的第i行累加到第i-1行,然后从数组的尾部开始逐个覆盖前置元素,直到第i行,然后计算sum的值,d数组的第i-1,i行第1列的两个元素的和加上第2列两个元素的差的绝对值,sum累加到ans,传递给下一层递归调用。这样递归调用直到叶子节点,求得某条路径下的一个ans,并更新当前ans取较大值,然后回溯再搜索。全部路径搜索完之后,得到最终的最大的ans值。
- 错误
n=0时,dfs函数不会执行任何代码,直接返回,ans=0,程序不会报错 - 正确
若d数组的元素全为0,则所有的sum计算结果都是0,ans不会被更新,也是0 - 错误
很显然,根据第2题的结论,当输入均为0时,输出结果为0,并不小于任何输入的数 - B
当d[i][0]是20个9,d[i][1]是20个0时,则每次计算s = a + x + abs(b - y)时,abs(b-y)则恒为0,可以直接忽略,所以每次累加的是d[i-1][0]和d[i][0],很显然,dfs搜索路径的最左边的路径搜索下来得到的累加结果ans应该是最大的,因为沿着这条路径搜索,每次都是d[1][0]累加到d[0][0],从第2层节点开始,s=9+9=,第3层,s=
,直到第20层节点,s=
,d[0][0]总是保持顶端优势,为最大。所以ans=
- C
当d[i][0]是30个0,d[i][1]是30个5时,则每次计算s = a + x + abs(b - y)时,a+x则恒为0,可以直接忽略,所以每次累加的是d[i-1][1]和d[i][1]的差的绝对值,从第2层开始直到第30层,s依次为:
5-5=0
10-5=5
15-5=10
...
所以 - C
结合第4,5两题,这里需要分别计算a+x和abs(b-y)两部分,然后累加,首先a+x,从第2层直到第15层叶子层:
15+14
15+14+13
...
15+14+13+...+2+1
然后abs(b-y)
15-14
15+14-13
15+14+13-12
...
15+14+13+...+2-1
这两块加起来求和,注意一定要算仔细,不要出错,结果是C, 2240