算法竞赛入门经典笔记

  • 前菜

/**
 * 输入正整数n(n<360),输出n度的正弦、余弦函数值。提示:使用数学函数
 * test1
 */
#define PI 4.0*atan(1.0)

void test1() {
    printf("%d",INT_MAX);//测试
    int n;
    scanf("%d", &n);
    printf("%lf\n", sin((n * PI) / 180));
    printf("%lf\n", cos((n * PI) / 180));
}

/**
 * 输出所有形如aabb的4位完全平方数(即前两位数字相等,后两位数字也相等)。
 * test2
 */

void test2() {
    //第一种方法
    for (int a = 1; a <= 9; a++)
        for (int b = 0; b <= 9; b++) {
            int n = a * 1100 + b * 11; //这里才开始使用n,因此在这里定义n
            int m = floor(sqrt(n) + 0.5);
            if (m * m == n) printf("%d\n", n);
        }
    //另一个思路是枚举平方根x,从而避免开平方操作
    for (int x = 1;; x++) {
        int n = x * x;
        if (n < 1000) continue;
        if (n > 9999) break;
        int hi = n / 100;
        int lo = n % 100;
        if (hi / 10 == hi % 10 && lo / 10 == lo % 10) printf("%d\n", n);
    }
}
/**
 * 对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半。
 * 经过若干次这样的变换,一定会使n变为1。例如,3→10→5→16→8→4→2→1。
 * 输入n,输出变换的次数。n≤109。
 * test3
 */
void test3(){

    int n2, count = 0;
    scanf("%d", &n2);
    long long n = n2; //防止乘法溢出,还可以使用c++流
    while(n > 1) {
        if(n % 2 == 1) n = n*3+1;
        else n /= 2; count++;
    }
    printf("%d\n", count);
}
  • 近似计算


    在这里插入图片描述
double sum = 0; 
    for(int i = 0; ; i++) { 
        double term = 1.0 / (i*2+1); 
        if(i % 2 == 0) sum += term; 
        else sum -= term; 
        if(term < 1e-6) break; 
    }
    printf("%.6f\n", sum);
  • 阶乘之和

输入n,计算S=1!+2!+3!+…+n!的末6位(不含前导0)。
n≤106,n!表示 前n个正整数之积。
样例输入: 10
样例输出: 37913

//第一种,乘法造成溢出,虽然可以通过每一步对模取余,但是效率太低了
int n, S = 0; scanf("%d", &n); 
for(int i = 1; i <= n; i++) { 
  int factorial = 1; 
  for(int j = 1; j <= i; j++) 
      factorial *= j;
  S += factorial; 
}
printf("%d\n", S % 1000000);

//第二种40开始,答案始终不变。25!末尾有60,
所以从第5项开始,后面的所有项都不会影响和的末6位数字
只需要在 程序的最前面加一条语句“if(n>25)n=25;”,
效率和溢出都将不存在问题

在这里插入图片描述

  • 文件的输入输出

例题2-5 数据统计 
输入一些整数,求出它们的最小值、最大值和平均值(保留3位小数)。
输入保证这些 数都是不超过1000的整数。 
样例输入: 2 8 3 5 1 7 3 6 
样例输出: 1 8 4.375

#define INF 1000000000
int main() {
    FILE *fin, *fout;
    fin = fopen("data.in", "rb");
    fout = fopen("data.out", "wb");
    int x, n = 0, min = INF, max = -INF, s = 0;
    while (fscanf(fin, "%d", &x) == 1) {
        s += x;
        if (x < min) min = x;
        if (x > max) max = x;
        n++;
    }
    fprintf(fout, "%d %d %.3f\n", min, max, (double) s / n);
    fclose(fin);
    fclose(fout);
    return 0;
}
  • 韩信点兵


    相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人 一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。
    输入包含多组 数据,每组数据包含3个非负整数a,b,c,表示每种队形排尾的人数(a<3,b<5,c< 7),输出总人数的最小值(或报告无解)。
    已知总人数不小于10,不超过100。输入到文件 结束为止。
    样例输入:
    2 1 6
    2 1 3
    样例输出:
    Case 1: 41
    Case 2: No answer
    在这里插入图片描述
  • 倒三角形


    输入正整数n≤20,输出一个n层的倒三角形。例如,n=5时输出如下:
    在这里插入图片描述
/**
 * 倒三角
 * test4
 */
void test4(){
    int n;
    scanf("%d",&n);
    int a,b=-1;
    for (int i = n-1; i >=0; --i) {
        a = 2*i+1;//输出多少个#
        b++;//输出多少个空格
        for (int j = 0; j < b; ++j) printf(" ");
        for (int j = 0; j < a; ++j) printf("#");
        printf("\n");
    }
}
  • 子序列之和


    在这里插入图片描述
void test5() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF && (m || n)) {
        double sum = 0.0;
        for (; n <= m; ++n) {
//          sum += 1.0/(n*n);//使用n*n会溢出
            sum += 1.0/n/n;
        }
        printf("%.5lf",sum);
    }
}
  • 分数化小数


    输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。
    a,b≤106,c≤100。输入包含多组数据,结束标记为a=b=c=0。
    样例输入:
    1 6 4
    0 0 0
    样例输出:
    Case 1: 0.1667
void test6() {
    int a,b,c;
    while (scanf("%d%d%d", &a, &b, &c) != EOF && (a || b || c)) {
//        printf("%*.*lf",4,2,12.3456);
        printf("%.*lf\n",c,(double)a/b);
    }
}
  • 排列


    用1,2,3,…,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要 求abc:def:ghi=1:2:3。按照“abc def ghi”的格式输出所有解,每行一个解。
void test7() {
    int abc, def, ghi, s;
    int t[10];
    for (abc = 123; abc <= 987 / 3; abc++)
        for (def = 246; def <= 987 * 2 / 3; def++)
            for (ghi = 369; ghi <= 987; ghi++)
                if (def == abc * 2 && ghi == 3 * abc)//判断abc:def:ghi = 1:2:3
                {
                    s = abc + def * 1000 + ghi * 1000000;
                    set<int> set;
                    while (s) {
                        set.insert(s % 10);
                        s = s / 10;
                    }
                    int len = set.size();
                    if (len == 9)//满足abc:def:ghi = 1:2:3且三个三位数中没有相同数字
                    {
                        printf("abc =%d\t def=%d\t ghi=%d\n", abc, def, ghi);
                    }

                }
}
  • 浮点数比较


    不能直接用== 或!=比较,因为浮点数只是近似值;
    所以对于两个浮点数a,b,如果要比较大小,那么常常会设置一个精度,如果fabs(a-b)<=1e-6,那么就是相等了
double i;
    for(i = 0; fabs(i-10.0)>1e-6; i += 0.1)
        printf("%.1f\n", i);
  • 数组


    如果声明的是“int a[maxn],b[maxn]”,是不能赋值b=a的。如果要从数组a复 制k个元素到数组b,可以这样做:memcpy(b,a,sizeof(int)*k)。当然,如果数组a和b 都是浮点型的,复制时要写成“memcpy(b,a,sizeof(double)*k)”。另外需要注意的是, 使用memcpy函数要包含头文件string.h。如果需要把数组a全部复制到数组b中,可以写得简单 一些:memcpy(b,a,sizeof(a));
    “memset(a,0,sizeof(a))”的作用是把数组a清零,它也在string.h中定义

  • 开灯问题


    有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2 的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯 将被打开,开着的灯将被关闭),依此类推。一共有k个人,问最后有哪些灯开着?输 入n和k,输出开着的灯的编号。k≤n≤1000。
    样例输入: 7 3
    样例输出: 1 5 6 7

void test8(){
    int a[1001];
    memset(a,1,sizeof(a));
    int n,k;
    scanf("%d%d",&n,&k);
    for (int i = 2; i <= k; ++i) {
        for (int j = 1; j <= n; ++j) {
            if(j%i==0) a[j] = !a[j];
        }
    }
    for (int i= 1; i <= n; ++i) {
        if (a[i]) printf("%d ",i);
    }
}
  • 蛇形填数


    在n×n方阵里填入1,2,…,n×n,要求填成蛇形。例如,n=4时方阵为:
10 11  12  1 
9  16  13  2 
8  15  14  3 
7  6   5   4
/**
 * 蛇形填数 test9
 */
void test9() {
    int n;
    scanf("%d", &n);
    int a[n][n];
    memset(a, 0, sizeof(a));
    int x, y, tot;
    tot = a[x = 0][y = n - 1] = 1;
    while (tot<n*n){
        while (x+1<n && !a[x+1][y]) a[++x][y] = ++tot;
        while (y-1>=0 && !a[x][y-1]) a[x][--y] = ++tot;
        while (x-1>=0 && !a[x-1][y]) a[--x][y] = ++tot;
        while (y+1<n && !a[x][y+1]) a[x][++y] = ++tot;
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
}
  • 竖式问题


    找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中, 所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有 竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。具体格式见样例输出 (为了便于观察,竖式中的空格改用小数点显示,但所写程序中应该输出空格,而非小数 点)
样例输入: 2357 
样例输出: 
<1> 
..775 
X..33 
----- 
.2325 
2325. 
----- 
25575 
The number of solutions = 1
/**
 * 竖式问题 test10
 */
void test10(){
    char s[20],buf[100];
    scanf("%s",&s);
    int count=0;
    for (int abc = 111; abc < 999; ++abc) {
        for (int de = 11; de < 99; ++de) {
            int x = abc*(de%10);
            int y = abc*(de/10);
            int z = abc*de;
            sprintf(buf,"%d%d%d%d%d",abc,de,x,y,z);
            int ok = 1;
            for(int i = 0; i < strlen(buf); i++)
                if(strchr(s, buf[i]) == NULL) {
                    ok = 0;
                    break;
                }
            if(ok) {
                printf("<%d>\n", ++count);
                printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
            }
        }
    }
    printf("The number of solutions = %d\n", count);
}
  • TeX中的引号


    在这里插入图片描述
int c, q = 1; 
while((c = getchar()) != EOF) { 
    if(c == '"') { 
        printf("%s", q ? "``" : "''");
        q = !q; 
    } 
    else printf("%c", c); 
}
  • WERTYU


    把手放在键盘上时,稍不注意就会往右错一 位。这样,输入Q会变成输入W,输入J会变成输 入K等。键盘如图3-2所示。 输入一个错位后敲出的字符串(所有字母均 大写),输出打字员本来想打出的句子。输入保 证合法,即一定是错位之后的字符串。例如输入中不会出现大写字母A。
    样例输入: O S, GOMR YPFSU/
    样例输出: I AM FINE TODAY.
#include<stdio.h>
char s[] = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
int main() {
    int i, c;
    while((c = getchar()) != EOF) {
        for (i=1; s[i] && s[i]!=c; i++); //找错位之后的字符在常量表中的位置
        if (s[i]) putchar(s[i-1]); //如果找到,则输出它的前一个字符
        else putchar(c);
    }
    return 0;
}
  • 回文词 例题3-3


    输入一个字符串,判断它是否为回文串以及镜像串。输入字符串保证不含数字0。所谓 回文串,就是反转以后和原串相同,如abba和madam。所有镜像串,就是左右镜像之后和原
    串相同,如2S和3AIAE。注意,并不是每个字符在镜像之后都能得到一个合法字符。在本题 中,每个字符的镜像如图3-3所示(空白项表示该字符镜像后不能得到一个合法字符)

  • 例题3-4 猜数字游戏的提示


    待补充。。

  • 生成元


    如果x加上x的各个数字之和得到y,就说x是y的生成元。给出n(1≤n≤100000),求最小 生成元。无解输出0。
    例如,n=216,121,2005时的解分别为198,0,1979。

/**
 * 最小生成元 test11
 */
#define maxN 100005
int ans[maxN];
void test11() {
    int x,y;
    memset(ans,0, sizeof(ans));
    for (int m = 1; m <= 100000; ++m) {
        x = y = m;
        while (x) {
            y += x % 10;
            x /= 10;
        }
        if (ans[y]==0 || m<ans[y]) ans[y] = m;
    }
    int n;
    while (scanf("%d",&n)!=EOF){
        printf("%d\n",ans[n]);
    }
}
  • 环状序列


    在这里插入图片描述
//环状串s的表示法p是否比表示法q的字典序小
int less1(const char* s, int p, int q) {
    int len = strlen(s);
    for (int i = 0; i < len; ++i) {
        if (s[(p+i)%len] != s[(q+i)%len]){
            return s[(p+i)%len] < s[(q+i)%len];
        }
    }
    return 0;
}
/**
 * 环状序列 test12
 */
int main(){
    char s[maxN];
    scanf("%s",&s);
    int ans=0;
    int n = strlen(s);
    for (int i = 1; i < n; ++i) {
        if (less1(s,i,ans)) ans =i;
    }
    for(int i = 0; i < n; i++)
        putchar(s[(i+ans)%n]);
    putchar('\n');
   	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值