读算法竞赛入门

读算法竞赛入门

经典算法与思想

TIPs

  • 在代码中获得圆周率pi的比较好的方式是使用库math.h中的三角函数:4.0atan(1.0),4.0acos(1.0)等
  • sacanf的返回值是成功输入的变量的个数,例如如果scanf("%d%d",&a,&b)成功执行则返回2。
  • #ifdef LOCAL...,freopen("test.in","r",stdin);,freopen("test.out","w",stdout)
  • 有些问题的输出不会溢出,但其运算过程很可能会发生溢出,所以这点需要注意。
  • 在linux系统中栈大小没有保存在可执行文件中,不过可以使用指令ulimit来改变栈的大小;在windows系统中栈大小保存在可执行文件中,使用gcc编译时可以使用指令-Wl,--stack=16777216来指定栈的大小。所以一般将较大的数组保存在main函数之外或者使用堆中的空间。

使用递归逆序输出数组

代码:

include<stdio.h>

include<string.h>

void reverse_str(const charstr){
int len = strlen(str);
if(len == 0){
//在基准情形中执行的一些动作没有“重复性”
return;
}
else{
reverse_str(str+1);
putchar(
(str));//“递归”之后需要做一些事情
}
}

int main(){
char str[] = "abcdefg";
reverse_str(str);
}

aabb完全平方数(p17)

注意floor在程序中的作用

include<stdio.h>

include<math.h>

int main(){
for(int i=1;i<10;i++){
for(int j=0;j<10;j++){
int a = i1100 + j11;
//int b = sqrt(a); //如果这里b是int型则floor的使用就没有意义了
double b = sqrt(a);
if(floor(b+0.5) * floor(b+0.5) == a)printf("%d\n",a);
}
}
}

开关灯问题 uva 10110

直接明了的方式是使用数组模拟这些动作,但效率不好。这些灯的开关是有规律的,开关被触发奇数次则电灯是开着的,如果被触发偶数次则是关着的,奇数偶数只与电灯编号因数个数的奇偶性有关。

一个整数的因数必然是成对出现的,例如15=1*15、15=3*5;9=1*9、9=3*3。可以发现只有完全平方数的因数是奇数个,只要判断电灯编号是否是完全平方数就可以判断电灯的状态。

注意

当前题目中n的取值范围是2^32-1则必须使用无符号int,因为long在很多平台上与int取值范围相同,所以使用long时也必须是无符号的。

打印蛇形数

右上角的初始值、循环内的两种判断、内存空间的初始化

注意事项:在堆中分配的内存一般需要使用memset主动设置为0,否则可能都是乱码;

最长回文词 uva 11151

核心思想是枚举,但枚举也是有方向的:向右,或向两侧

竖式问题(p87)

一般而言没有效率要求的问题上,怎样简单怎样求解。

相同问题的统一处理。

问题:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合

解法:

注意函数的使用方法:sprintf(buffer,"%d%d%d%d%d",a,b,c,d,e);strchar(string,char)

总结:

一些相同的操作可以合并然后做统一的处理,例如本问题中的“子集”问题,这些字符串合并之后再进行处理可以简化编码。

素数求解问题

使用i*i = x 来判断x是否是素数是有问题的,因为当x很大时i*i可能溢出:

比例说当判断2147395601是否是素数时,i需要大于46340,可46340*46340将溢出。

相对比较简单且不易溢出的方法:

int is_prime(int x){
assert(x>0);
//int prime = 1; //多此一举
if(x == 1) return 0;
else{
int m = floor(sqrt(x) + 0.5); //必须加0.5
for(int i = 2;i<m;i++){
if(x%i == 0){
//prime = 0;
//break; //直接返回0即可
return 0;
}
}
}
//return prime; //直接返回1即可
return 1;
}

WERTYU uva 10082

使用常量数组有时可以简化逻辑

善用常数字符串将简化操作(p94),isdigit(int) ,isprint(int), isalpha(int) toupper(int) tolower() in ctype.h

include<stdio.h>

char s[55]="`1234567890-=QWERTYUIOP[]\ASDFGHJKL;'ZXCVBNM,./";
int main()
{
int i,c;
while((c=getchar())!=EOF)
{
//直接遍历,获得 i,这样可以简化逻辑(不用写for循环体)
for(i=1;s[i]&&s[i]!=c;i++);
if(s[i]) putchar(s[i-1]); //字符串的结尾标志是'\0'
else putchar(c);
}
return 0;
}

周期串 uva 455

首先要注意的是 对0取余可能使程序假死,所以 m%0 使不能使用的,这点编译与执行的过程中都不会报错,但程序的执行结果是错误的

m%n 可以看做是 m - (int)(m/n)*n,前提是m和n均为正整数

小学生算术 uva10035

注意进位

include<stdio.h>

int main(){
int a,b;
int c = 0;
int ans = 0;
while(1){
scanf("%d%d",&a,&b);
if(a == 0 && b == 0)break;
c = 0;
for(int i = 0;i<9 && a>0 && b>0;i++){
//c = (a%10 + b%10 + c)/10 ;
c = (a%10 + b%10 + c)>9?1:0;//判断要快于除法
ans += c;
a = a/10;b = b/10;
}
if(ans == 0)
printf("No carry operation.\n");
else{
printf("%d carry operations.\n",ans);
}
}
}

阶乘的精确值 uva623

n<=500,打印n!的精确值

字母重排

如何确定两个单词的组成相同?是迭代判断?显然效率太低,最好的办法是先排序这两个单词,然后再比较排序后的字符串。

C标准库函数qsort:

void qsort( void buf, //指向数据
size_t num, //需要比较num项数据
size_t size, //每项数据的大小
int (
compare)(const void *, const void *) );
//对于compare的要求,第一项大于第二项则返回正值;等于返回0;小余返回负值。

cantor数表 uva264

首先确定位置.判断在第几斜列有两种思想:

  • 迭代法,迭代n:1+2+...+n来判断m在第n列
  • 用数学法判断:n<=1/2 *k(k+1),通过解不等式来获得n,这样更快

猜数字游戏(理解的不是很透彻)p99

循环列表

如何实现字符串数组的循环访问(使用整数取余运算)

生成元问题

查表思想、也许运算的结果不会溢出,但运算的中间值可能溢出,这样依旧会出错

TEX中的引号

注意事项:getchar的返回值为int型,因为EOF是int型;逐字符读取的函数有getchar(),int fgetc(FILE *stream)

参考代码:

// UVa272 Tex Quotes
// Rujia Liu
#include<stdio.h>
int main() {
int c, q = 1;
while((c = getchar()) != EOF) {
if(c == '"') { printf("%s", q ? "``" : "''"); q = !q; }
else printf("%c", c);
}
return 0;
}

回文词与镜像词

  • 注意:
    * 首先问题是如何判断回文与镜像

Circular Sequence

  • 注意两个字符串以字典序比较大小的方法,容易出的逻辑错误是只比大小不判相等。
  for(int i = 0; i < n; i++)
    if(s[(p+i)%n] != s[(q+i)%n])
      return s[(p+i)%n] < s[(q+i)%n];

UVA 679 Dropping Balls

  • 两种典型的思想:“枚举”,递归思想

思想篇

暴力求解

暴力求解也有一定的技巧,例如给定n,求abcde/fghij=n的表达式,其中aj为09,如果穷举abcde和fghij则需要10!次,但如果将原式写成abcde=n*fghij,只穷举fghij再判断abcde是否满足要求则可以使穷举次数减少到不到10000次。

最大连续和问题

  • 给出一个实数序列,A1,A2,......An,求最大连续和 ;这里有一个思想可以降低算法的时间复杂度:连续子序列之和等于两个前缀和之差

最一般的解法就是穷举法,从A1开始进行穷举,算法的复杂度是O(n3);但先求前缀和可以使算法的复杂度降低为O(n2)。

posted @ 2017-02-26 08:57  jiahu  阅读(446)  评论(0)    收藏  举报