紫书学习笔记(1)
这几天杂七杂八的事情太多,再加上进入了考试周,所以很难有时间安安静静的敲敲代码,看看书,写写博客了。最近写了一些oj的题目,但是写到这个份上,发现有些做不动了。因为自己没有很系统的看过一些算法竞赛书,所以很多时候时间复杂度和空间复杂度都控制不好,所以就想写一写前段时间买的刘汝佳的紫书,也就是《算法竞赛入门经典》的学习笔记。目前只把前两章看完了。
其实前两章的内容比较浅,还大部分都是基础的知识。但是也体现了算法竞赛和平常打代码的不同。在算法竞赛中我们不要打印一些无关的信息,我们平时这样做可能是为了界面的友好等等。但是竞赛对于输出和输入的格式很严格,那样做无疑是自取烦恼。
我们来一个很简单的例子,就是两个变量a和b相互交换。我们首先想到的应该就是三变量交换的方法,但是书上还写出了另外三种答案:
第一种:
int a,b; scanf("%d %d",&a,&b); a=a+b; b=a-b; a=a-b;
第二种:
int a,b; scanf("%d %d",&a,&b); a^=b; b^=a; a^=b;
如果说第一种还比较好理解的话,那么第二种是用异或运算符是什么意思呢?记得在之前的一篇blog:http://www.cnblogs.com/kugwzk/p/5046676.html,我写过所有数值在计算机都是用二进制储存的,所以^可以看成两个数相加。那么b^=a;则是把a中与b相同的位全部变为0(a此时为a+b),从而获得了a原来的值。同理a可以获得b原来的值。这两种方法当时让我看到的时候觉得很是巧妙,因为省略了一个变量。但当我继续往下读的时候,lrj认为这些方法都不好,因为算法竞赛大都使用的是黑盒测试,所以并不关心使用了什么方法。那么解决这个问题的最好方法无疑是这样:
int a,b; scanf("%d %d",&a,&b); printf("%d %d",b,a);
的确是让人很无语,不过这也貌似告诉我们如果走投无路的时候打表貌似是一种很好的方法QAQ。。。
同时我们在知道一些数学公式的时候,比如说等差求和等,我们最好采用公式的方法,而不是循环来减少程序的运行时间。
而第二章,则主要讲的是循环结构。然后重点介绍了循环的代价,很浪费时间容易导致运行超时。还会出现数据溢出,这在竞赛中无疑是万分致命的。有几道题目还是很有意思的:
1.分数化小数
输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。a,b<=10^6,c<=100.输入包含多组数据,结束标记为a=b=c=0。
样例输入:
1 6 4
0 0 0
样例输出:
Case 1: 0.1667
代码:
#include <stdio.h> int main() { int kase=0,i,a,b,c,mod,c1; while(scanf("%d %d %d",&a,&b,&c)==3&&a!=0&&b!=0&&c!=0) { mod=a%b; printf("Case %d: %d.",++kase,a/b); for(i=1;i<c;i++) { mod*=10; printf("%d",mod/b); mod%=b; } /*进行第c位的判断来确定四舍五入*/ mod*=10; c1=mod/b; mod%=b; if((mod*10/b)>=5) c1++; printf("%d",c1); printf("\n"); } return 0; }
2.排列
用1~9组成三个三位数abc,def,ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3.按照”abc def ghi”的格式输出所有解每行一个解。
代码:
#include <stdio.h> int main() { int i ,a[10],j,k,l,flag; for(i=100;i<=329;i++) { l=0; flag=1; for(j=0;j<10;j++) a[j]=0; for(j=1;j<=3;j++) { l+=i; a[l/100]++; a[(l/10)-(l/100)*10]++; a[l%10]++; } //完成三个数的各位的储存 for(k=1;k<=9;k++) { if(a[k]!=1) { flag=0; break; } } if(!flag) continue; else printf("%d %d %d\n",i,i*2,i*3); } }
当然这道题暴力当然也可以,写一堆判定条件就好了,但是运用数组自认为是很简洁的一种方法。
前两章感觉还没有完全进入竞赛的阶段,继续往下估计收获会更大。