百度C语言吧主御坂美琴みさか的《C语言循环的小艺术》很久以前我就复制下来收藏了,不过一直没有研究。昨天Uval的AOAPC I: Beginning Algorithm Contests (Rujia Liu)做到Triangle Wave的时候又看到了这种对称图形,虽然用那种“分段写”的方法也顺利AC,但是既然知道也许有更好的方法自然要去探索一下,于是有了这篇解读。

不过这篇解读最只适用于看了很久原文也不知其解的人,如果你有把握自己看懂,那最好还是自己先搞清楚,这样印象更深刻,再回来看本文进行对照最好。

先看一下《C语言循环的小艺术》中的原文:

菱形打印

很多人,打印菱形在控制台的思路是,把菱形上下拆分,分两段很接近的代码来打印,
其实这样代码很不好看,并且不好阅读
我们知道,要打印的图案是这种:
    *
   ***
  *****
   ***
    *
满足上下对称,左右对称,那么,你能不能也弄一个二重循环,同样是对称的?
很简单,首先我们要抛开习惯性思维,for循环不一定要在0开始或者0结束
我们可以让循环从 -c 到 c ,这样不就轻松产生一个对称的吗?(只要取个绝对值)
我们把菱形的中心看成是坐标0,0,那么,会输出星号的坐标,是 |x| + |y| <= c 的点

由此可得

#include <stdio.h>
#define IABS(x) ( (x) >= 0 ? (x) : -(x) ) //定义一个计算绝对值的宏
void print(int size) // size是这个菱形的半径,直径会是size * 2 + 1
{int x, y;for (y = -size; y <= size; y++){for (x = -size; x <= size; x++){if ( IABS(x) + IABS(y) <= size ) //x和y各自的绝对值的和,即 |x| + |y| <= sizeputchar('*');elseputchar(' ');}putchar('\n');}
}int main()
{print(5); //输出一个半径为5的菱形getchar();return 0;
}

如果我需要得到空心菱形呢?非常非常简单,因为菱形边界上的点,满足的是|x| + |y| == c
所以,我们只要把那个if里的小于等于号,改成双等于号 == 就可以了

再类似地,如果我不要*号,我要最外层是字母A,然后里一层是B这样呢?即:
     A
   ABA
 ABCBA
   ABA
     A
那么,我们只要在putchar那里做一个字符计算:

void print(int size) // size是这个菱形的半径,直径会是size * 2 + 1
{int x, y;for (y = -size; y <= size; y++){for (x = -size; x <= size; x++){if ( IABS(x) + IABS(y) <= size ) //x和y各自的绝对值的和,即 |x| + |y| <= sizeputchar( 'A' + (size - IABS(x) - IABS(y)) ); //留意这里的计算方法elseputchar(' ');}putchar('\n');}
}

类似地,如果我们要打印的是X形:
  *   *
   * *
    *
   * *
  *   *
同样可以利用这个思路完成,这题就作为思考题吧

——————————————————————————————

其实include stdlib.h后可以直接用abs()- -,而且严格来说int main(void)是最正确的,当然这些不是重点。

拿到一个算法程序,最简单的分析方式就是先跟踪看一下运行过程,由于size理论上可以取任意值,我们只要弄懂一种情况就等于弄懂了所有情况,所以为了简化分析先size=3,看一下第一次大的循环:

y=-3
x=-3 ' '
x=-2 ' '
x=-1 ' '
x=0  '*'
x=1  ' '
x=2  ' '
x=3  ' '

到这里我们会发现,开始|x|+|y|>size,所以前面输出了三个' ',然而由于|x|渐渐减小,之后输出了一个'*',由于|x|又渐渐增大,又输出了三个' ',这样就巧妙地完成了左右的对称。

又由于下一次循环|y|比这次循环小1,所以就“更容易”输出'*',由于|y|也有对称的特性,所以完成了上下的对称。

至此,我们大概就对为什么可以上下左右对称有了一个感性的认识,不过恐怕更多的是对这些代码的惊叹:为什么这样写就能巧妙地做到这些?IABS(x) + IABS(y) <= size这个表达式也太神奇了!御坂美琴みさか是怎么想到的?!

其实我们上来看程序的运行过程,只是为了从运行过程中试着找到入口去探索这个算法的本质是什么,这是一个从现象到本质的过程,而不能被现象吓到了。

实际上御坂美琴みさか在文中已经写到了:

我们把菱形的中心看成是坐标0,0,那么,会输出星号的坐标,是 |x| + |y| <= c 的点

那么程序的原理实际上就是通过枚举{(x,y), |x| <= size, |y| <= size, x, y ∈Z}中的点(即下图中红色部分,包括边界),再通过一个式子来确认合法点,如果合法,就输出'*',反之输出' ':

那么我们又有疑问了,为什么是|x| + |y| <= c?

我们来证明一下右上角的红色三角区域(包括边界)的所有点(x,y)(x,y∈N),x+y<=size:

通过观察,我们可以发现y的取值范围随着x在改变,至于“怎么改变”则与size有关,那么我们可以想一下,如果我们把其中的y用size和x表示,这个不等式不就容易得证了吗?

那么我们可以得知y∈[0, size-x](x≠size),取两边分别讨论,当y=0时,x+y=x<=size,当y=size-x时,x+y=x+size-x=size,当y∈(0, size-x)(x≠size)时,x+y<size。综上所述,x+y<=size,证毕。

其实即使你不会证明,也可以当作公式默认下来,这都无关紧要。

于是我们就能自己写出|x| + |y| <= c这个式子了,也就知道了这个算法的本质,现在一切都掌握在我们手中,甚至我们可以对原程序做一点改动:

由于是枚举(x,y)∈{(x,y), |x| <= size, |y| <= size, x, y ∈Z},而且有绝对值的对称做保障,那么无论x、y谁在外面只要全枚举到就行了,所以也可以把x放在外层循环:

for (x = -size; x <= size; x++)
{for (y = -size; y <= size; y++){if (IABS(x) + IABS(y) <= size) //x和y各自的绝对值的和,即 |x| + |y| <= sizeputchar('*');elseputchar(' ');}putchar('\n');
}

观察原程序的输出,我们可以发现由于对称的缘故,程序输出的右侧多输出了我们看不见的空格,我们也可以不输出它:

if (IABS(x) + IABS(y) <= size ) //x和y各自的绝对值的和,即 |x| + |y| <= size
    putchar('*');

else if (x < 0)
    putchar(' ');
else
    break;

虽然上述改动没有太大意义,但是说明了如果我们掌握了算法的本质,我们就掌握了全局,那么就可以进行任何改动的道理。

那么思考题也很简单了(自己先试着写写看):

for (x = -size; x <= size; x++)
{for (y = -size; y <= size; y++){if (IABS(x) == IABS(y) && IABS(x) <= size) // 正比例函数性质嘛,|y|=|x|putchar('*');elseputchar(' ');}putchar('\n');
}

于是我们开始思考,御坂美琴みさか是如何想到利用坐标的方法来处理这个问题的?

很明显御坂美琴みさか与我们的区别就是,我们只知道傻傻地去找规律,而御坂美琴みさか则能换个角度,通过数学的思想来解决问题,这就是我们与御坂美琴みさか的差别就在于,我们没有用数学的思想解决问题的意识,为什么没有意识?说明我们的数学素养不够,不善于发现数学问题。

从御坂美琴みさか身上我们可以学到:

1. 避开思维定势,换个角度思考问题(这点与《孙维刚初中数学》不谋而合,建议想学好数学的朋友阅读《孙维刚初中数学》和《孙维刚高中数学》这套书)。

2. 要善于发现数学问题,多加利用数学思想,很多时候利用数学可以简洁漂亮地解决问题,并有很大高的灵活性。

同时我们要学会学习他人算法程序的方法:

1. 可以先看一下表面现象,比如跟踪一下,其中有一些技巧,比如缩小测试数据(例如上面size=3)。

2. 通过表面现象找到切入点,去分析这个算法的本质。

3. 学到本质后,试着去改动一下原程序,寻找另一种实现方式,或者去尝试优化一下甚至改动一些无关痛痒的东西。很多时候,算法是算法,实现是实现。

实际上《算法竞赛入门经典》勘误表中的:

4. 71页的那个j % i也许有点不好理解,如果你理解透了会发现改成j-i也是没问题的……

也是通过这种方式得出的。

所以学习算法,千万不要去背代码,背代码只是去背实现,隔靴搔痒而已,而是应该去知道这个算法是怎么一回事,知道怎么回事后实现就能写出,信不信由你,反正我是信了- -。

现在再看Triangle Wave,是不是也很简单呢?

解读御坂美琴みさか的菱形打印程序——谈如何学习算法相关推荐

  1. SCU-4396 麦野沉利与御坂美琴的战斗

    4396: 麦野沉利与御坂美琴的战斗 Submit your solution     Discuss this problem     Best solutions 描述 麦姐辛苦建立的实验室被入侵 ...

  2. 御坂美琴(入门bfs+STL)

    御坂美琴 misaka是呱太爷爷的小粉丝,呱太爷爷有一句话说的好:"一尺之棰,日取其半,万世不竭". misaka现在有 n 个呱太玩偶放在一堆,每一次操作,misaka会选择当前 ...

  3. Wannafly挑战赛29 御坂美琴(递归,模拟)

    链接:https://ac.nowcoder.com/acm/contest/271/A 来源:牛客网 御坂美琴 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语 ...

  4. Wannafly挑战赛29-A御坂美琴 (dfs+map)

    链接:https://ac.nowcoder.com/acm/contest/271/A 来源:牛客网 御坂美琴 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语 ...

  5. 牛客21477 御坂美琴

    链接 点击跳转 题解 这个题是个思维题 首先如果所要求的数字之和不等于n,就直接判为无解.这一点很重要,可以保证后面算法的复杂度. 然后不停的把最大的分成两半,随时把符合条件的堆删掉,如果要求的堆的最 ...

  6. 牛客NC21477 御坂美琴

    题目描述: misaka是呱太爷爷的小粉丝,呱太爷爷有一句话说的好:"一尺之棰,日取其半,万世不竭". misaka现在有 n 个呱太玩偶放在一堆,每一次操作,misaka会选择当 ...

  7. 11.22A 御坂美琴-优先队列模拟

    链接:https://ac.nowcoder.com/acm/contest/271/A 来源:牛客网 题目描述 misaka是呱太爷爷的小粉丝,呱太爷爷有一句话说的好:"一尺之棰,日取其半 ...

  8. 牛客Wannafly挑战赛29 A 御坂美琴 思维题

    链接:https://ac.nowcoder.com/acm/contest/271/A 来源:牛客网 题目描述 misaka是呱太爷爷的小粉丝,呱太爷爷有一句话说的好:"一尺之棰,日取其半 ...

  9. Wannafly挑战赛29A御坂美琴

    传送门 这套题很有意思2333 蠢了--首先先判总共加起来等不等于\(n\),不是的话就不行 然后dfs记录\(n\)不断分下去能分成哪些数,用map记录一下,判断是否所有数都能被分出来就是了 //m ...

最新文章

  1. qrcode生产带logo_“白板”口罩打上LOGO装名牌 警方重拳出击清市场
  2. 使用axis开发web service服务端
  3. 数据科学家必须要掌握的5种聚类算法
  4. Bootstrap下拉菜单组件
  5. 人工智能与经济生产力:期待进化,而不是革命
  6. D3.js 教程: 使用 JavaScript 创建可交互的柱状图
  7. 【Android 逆向】函数拦截原理 ( 可执行程序基本结构 | GOT 全局偏移表 | 可执行程序函数调用步骤 )
  8. 常微分方程数值解:欧拉公式
  9. pandas python2.7_python-2.7 – Pandas Seaborn安装
  10. nosql和rdnms_用于SaaS和NoSQL的Jdbi
  11. 初识Hadoop:大数据与Hadoop概述
  12. 《深入浅出DPDK》读书笔记(八):网卡性能优化(异步中断模式、轮询模式、混和中断轮询模式)
  13. 三种DDL的简述:create、alter、drop
  14. mysql-multi source replication 配置
  15. App Inventor自定义插件Extension
  16. CSS盒子模型居中方法,高级面试题+解析
  17. Spark案例之流量统计(三种方法)
  18. python sin_Python入门之三角函数sin()函数实例详解
  19. js经纬度打开腾讯地图
  20. 买房贷款在什么情况下会被拒? 你避开这些雷区了吗?

热门文章

  1. 百度图说——零编程玩转ECharts图表
  2. java 实现汉字转拼音,java汉字简体转繁体 java汉字繁体转简体
  3. pc上安装anroid系统
  4. 产品销售好坏需要哪些方面分析原因-悟空软件与你一起探讨
  5. Java中使用递归解决不死神兔问题
  6. Unity性能测试工具:Profile
  7. 浙江浙大中控技术前端面经
  8. new String(abc)和“abc”的区别
  9. 最健康的作息时间表~·
  10. “笨办法”学Python笔记 Lesson35—45