「算法竞赛入门经典」「第三章」

开灯问题(P39)

n盏灯,编号为1-n,第一个人把所有灯打开,第二个人按下编号为2的倍数的灯的开关,第三个人按下编号为3的倍数的灯的开关,以此类推,一共有k个人,问最后有哪些灯开着。

// 3开灯问题.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    int n,k;
    int a[1005];
    int first = 0;
    while(scanf("%d%d",&n,&k)){
        memset(a,0,sizeof(a));
        for(int i=1;i<=k;i++){
            for(int j=1;j<=n;j++){
                if(j%i==0)a[j] = !a[j];
            }
        }
        for(i=1;i<=n;i++){
            if(a[i]){
                if(first==0){printf("%d",i);first=1;}
                else printf(" %d",i);
            }
        }
        printf("\n");
    }
    return 0;
}

蛇形填数(P39)

在n*n的方阵里填入1,2,3...,n*n,要求填成蛇形。例如,n=4时的方阵为:
10 11 12 1
9 16 13 2
8 15 14 3
7 6 5 4
先判断,再填数。这个题必须得建立二维数组才输出成这样,所以不要犹豫。

// 3蛇形填数.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    int n;
    int a[10][10];
    while(scanf("%d",&n)){
        memset(a,0,sizeof(a));
        int tot = 0;
        int x = 0;
        int y = n-1;
        a[x][y] = ++tot;
        while(tot<n*n){
            while(x+1 <= n-1 && a[x+1][y] == 0) a[++x][y] = ++tot;  
            while(y-1 >= 0 && a[x][y-1] == 0) a[x][--y] = ++tot;
            while(x-1 >= 0 && a[x-1][y] == 0) a[--x][y] = ++tot;
            while(y+1 <= n-1 && a[x][y+1] == 0) a[x][++y] = ++tot;  
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                printf("%3d",a[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}
竖式问题(P41)

最开始无从下手,但其实已经遇到很多这样的循环枚举的题目了,就从题意开始,枚举三位数和两位数。
strchr(a,'b')是在字符串a中查找b字符,如果找不到就返回NULL,strlen返回字符串真实长度。

// 3竖式问题.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char num[11];
    while(scanf("%s",num)){
        int count = 1;
        for(int i=100;i<1000;i++){
            for(int j=10;j<100;j++){
                int ok = 1;
                int abc = i;
                int de = j;
                int temp1 = abc * (de%10);
                int temp2 = abc * (de/10);
                int temp3 = abc * de;
                char buf[50];
                sprintf(buf,"%d%d%d%d%d",abc,de,temp1,temp2,temp3);
                for(int q=0;q<strlen(buf);q++){
                    if(strchr(num,buf[q])==NULL) ok = 0;
                }
                if(ok){
                    printf("<%d>\n",count++);
                    printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n",abc,de,temp1,temp2,temp3);
                }
            }
        }
        printf("\nThe number of solutions = %d\n",--count);
    }
    return 0;
}
例题3-1 TeX中的引号(UVa272)(P45)

这个题主要就是知道了

int c;
c = getchar();
是正确的
char c;
c = getchar();
是错误的,因为getchar的返回值是int型。

EOF表示End Of File,表示输入流的结尾,其代表的是-1,用char类型来表示负数是错误的,所以是int。
scanf("%s",s)在读取字符串的时候,遇到空格,Tab,换行都会停止读取。

例题3-2 WERTYU(UVa10082)(P47)

键盘输入右错一位,输入一个错位后的字符串,均大写,输出错位修正后的字符串。

// lt32.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
    char s[] = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
    int c;
    while((c=getchar())!=EOF){
        for(int i=0;i<strlen(s);i++){
            if(s[i] == c){putchar(s[i-1]);break;}
        }
        if(i == strlen(s))putchar(c);
    }
    printf("\n");
    return 0;
}
3-3回文词(UVa401)

判断一个字符串是否是回文串,以及镜像串

// lt33.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char mirror[] = "A   3  HIL JM O   2TUVWXY51SE Z  8 ";
char fun(char c){
    if(isalpha(c))return mirror[c-'A'];
    else return mirror[c-'0'+25];
}
int main(int argc, char* argv[])
{
    char input[30];
    char* msg[] = {"is not a palindrome","is a regular palindrome","is a mirrored string","is a mirrored palindrome"};

    char ch[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
    
    while(scanf("%s",input)){
        int p=1,m=1;
        for(int i=0;i<(strlen(input)+1)/2;i++){
            //这块不能判断相等然后赋值为1,逻辑会出现错误
            if(input[i] != input[strlen(input)-1-i]) p=0;
            if(fun(input[i]) != input[strlen(input)-1-i]) m=0;
        }
        /*
        printf("%s -- ",input);
        if(m+p==0)printf("%s.\n",msg[0]);
        if(m+p==1){
            if(m==1) printf("%s.\n",msg[2]);
            else printf("%s.\n",msg[1]);
        }
        if(m+p==2)printf("%s.\n",msg[3]);
        */
        printf("%s -- %s.\n.\n",input,msg[2*m+p]);
    }
    return 0;
}
例题3-4猜数字游戏的提示(UVa 340)

给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现但位置不对(B)
又是一个枚举循环,对于每个数字统计两个序列中数字出现的次数,两个次数取最小值,注意时每个数字都要取最小值然后累加到B上,最后B减A。

// lt34.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#define min(a,b) a>b?b:a
int main(int argc, char* argv[])
{
    int count;
    int num = 1;
    while(scanf("%d",&count)&&count){
        printf("Game %d:\n",num++);
        int a[1005],s[1005];
        for(int i=0;i<count;i++){
            scanf("%d",s+i);
        }
        while(1){
            int sum=0;
            int A=0,B=0;
            for(int i=0;i<count;i++){
                scanf("%d",a+i);
                sum += a[i];
            }
            if(sum == 0)break;
            for(i=0;i<count;i++){
                if(a[i] == s[i]) A++;
            }
            
            for(i=1;i<=9;i++){
                int b1=0,b2=0;
                for(int j=0;j<count;j++){
                    if(a[j] == i)b1++;
                    if(s[j] == i)b2++;
                }
                B += min(b1,b2);
            }
            printf("(%d,%d)\n",A,B-A);
        }
    }
    return 0;
}

例题3-5 生成元(UVa1583)(P52)
如果x加上x的各个数字之和得到y,就说x是y的生成元。给出n(1<=n<=100000),求最小生成元,无解输出零。
第一次做查表的题目。遇到一个数就枚举的话,效率并不高,因为很明显每个数只会对应一个固定的结果,所以完全可以把这些结果都提前计算好,并存储起来,输出结果就直接查表。

// lt35.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{   
    int ans[100005];
    memset(ans,0,sizeof(ans));
    for(int i=1;i<=100000;i++){
        int x=i,y=i;
        while(x>0){
            y += x%10;
            x = x/10;
        }
        if(ans[y] == 0 || i < ans[y]) ans[y] = i;
    }
    int count;
    int n;
    scanf("%d",&count);
    while(count--){
        scanf("%d",&n);
        printf("%d\n",ans[n]);
    }
    return 0;
}
例题3-6 环状序列(UVa1584)

最近在网上提交这道题,至少还是参加过比赛的,完全忘记了提交注意事项。

1.因为是用VC写的,#include "stdafx.h"会有这么一个头,在提交时不要加上。
2.我觉得VC违背了c语言的块级作用域,如图使用会报重复定义的错。但不应该报错的,应该使用其他IDE不会出现这种情况,不得已提出i的定义,放在最开始。点这个网友回答蛮好的

一句话够了,取余来实现循环。

// lt36.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int less(char *s,int p,int q){
    int n = strlen(s);
    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];
    }
    return 0;
}
int main(int argc, char* argv[])
{

    int count;
    scanf("%d",&count);
    char s[105];
    while(count--){
        int ans = 0;
        int i;
        scanf("%s",s);
        for(int i=0;i<strlen(s);i++){
            if(less(s,i,ans)) ans = i;
        }
        for(int i=0;i<strlen(s);i++){
            putchar(s[(ans+i)%strlen(s)]);
        }
        printf("\n");
    }
    return 0;
}
习题3-1 得分(UVa1585)

简单题

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    int T;
    scanf("%d",&T);
    while(T--){
        char s[85];
        scanf("%s",s);
        int tot = 0,sum =0;
        for(int i=0;i<strlen(s);i++){
            if(s[i] == 'O') {tot++;sum+=tot;}
            else tot = 0;
        }
        printf("%d\n",sum);
    }
    return 0;
}
习题3-2 分子量(UVa1586)
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int fun(int A,char *str,int i){
    if(isdigit(str[i+1])){
        if(isdigit(str[i+2])){
            A += str[i+2] - '0';
            A += (str[i+1] - '0')*10;
        }
        else
            A += str[i+1] - '0';
    }
    else A++;
    return A;
} 
int main(int argc, char* argv[])
{
    float ch[] = {12.01,1.008,16.00,14.01};
    int T;
    scanf("%d",&T);
    while(T--){
        char str[85];
        int C=0,H=0,O=0,N=0;
        scanf("%s",str);
        for(int i=0;i<strlen(str);i++){
            if(str[i] == 'C'){
                C = fun(C,str,i);
            }
            else if(str[i] == 'H'){
                H = fun(H,str,i);
            }
            else if(str[i] == 'O'){
                O = fun(O,str,i);
            }
            else if(str[i] == 'N'){
                N = fun(N,str,i);
            }
        }
        float sum = ch[0]*C+ch[1]*H+ch[2]*O+ch[3]*N;
        printf("%.3f\n",sum);
    }
    return 0;
}

这个使用传址实现,比较方便,最开始*A没有加括号,导致程序出现错误,所以一定要记得指针在运算时一定要加括号。

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void fun(int *A,char *str,int i){
    if(isdigit(str[i+1])){
        if(isdigit(str[i+2])){
            (*A) += str[i+2] - '0';
            (*A) += (str[i+1] - '0')*10;
        }
        else
            (*A) += str[i+1] - '0';
    }
    else (*A)++;
} 
int main(int argc, char* argv[])
{
    float ch[] = {12.01,1.008,16.00,14.01};
    int T;
    scanf("%d",&T);
    while(T--){
        char str[85];
        int C=0,H=0,O=0,N=0;
        scanf("%s",str);
        for(int i=0;i<strlen(str);i++){
            if(str[i] == 'C'){
                fun(&C,str,i);
            }
            else if(str[i] == 'H'){
                fun(&H,str,i);
            }
            else if(str[i] == 'O'){
                fun(&O,str,i);
            }
            else if(str[i] == 'N'){
                fun(&N,str,i);
            }
        }
        float sum = ch[0]*C+ch[1]*H+ch[2]*O+ch[3]*N;
        printf("%.3f\n",sum);
    }
    return 0;
}
习题3-3 数数字UVa1225
#include <stdio.h>
#include <string.h>
char s[100000];
int main(int argc, char* argv[])
{
    int T;
    scanf("%d",&T);
    while(T--){
        int q[10];
        memset(q,0,sizeof(q));
        memset(s,'\0',sizeof(s));
        int num,i;
        scanf("%d",&num);
        for(i=1;i<=num;i++){
            char buf[10];
            sprintf(buf,"%d",i);
            strcat(s,buf);
        }
        for(i=0;i<strlen(s);i++){
            q[s[i]-'0']++;
        }
        for(i=0;i<9;i++){
            printf("%d ",q[i]);
        }
        printf("%d\n",q[9]);
    }
    return 0;
}
习题3-4 周期串UVa455

像这种类似循环的题目,跟取余一定有关系。原题里的空行我一直没读懂,尴尬英语不好。

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    int T;
    scanf("%d",&T);
    while(T--){
        char str[85];
        scanf("%s",str);
        int length = strlen(str);
        for(int i=1;i<=length;i++){
            if(length%i == 0){
                int j;
                for(j=0;j<length;j++){
                    if(str[j] != str[j%i]) break;
                }
                if(j == length){
                    printf("%d\n",i);
                    break;
                }
            }
        }
        if (T) printf("\n");  
    }
    return 0;
}
习题3-5 谜题UVa227

首先解决的是二维数组的输入,考虑到会有空格在输入里,刚开始尝试的是getchar实现,但是要循环读入时,要控制换行\n的读入,存储空格的位置。而且这个题目有坑,就在题目中赋值的测试数据,如果空格在最右边的话,拖拽复制的话根本没有复制到空格,放在控制台直接验证会出错,但依据题意来说,其实是有空格的。

紫书上说gets函数已经被废弃了,但是从这道题发现

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char* argv[])
{
    int count=1;
    bool line = false;
    
    while(1){
        char init[5][5];
        char c;
        
        int puzzle=1;
        int i,j;
        int bi,bj;
        gets(init[0]);
        if(init[0][0] == 'Z')return 0;
        for(i=1;i<5;i++){
            gets(init[i]);
        }
        for(i=0;i<5;i++){
            for(j=0;j<5;j++){
                c = init[i][j];
                if(c == ' ') {
                    bi = i;bj = j;
                    i = 5;j=5;
                }
            }
        }
        
        char command [1000];
        bool valid = true;
        bool exit_koro = false;

        while ( !exit_koro && gets (command)) {

            for ( int i = 0; command [i] != 0; i++ ) {

                if ( command [i] == '0' || !valid ) {
                    exit_koro = true;
                    break;
                }
                switch (command [i]) {
                case 'A' :
                    if ( bi == 0 )
                        valid = false;
                    else {
                        init [bi] [bj] = init [bi - 1] [bj];
                        init [bi - 1] [bj] = ' ';
                        bi--;
                    }
                    break;

                case 'B' :
                    if ( bi == 4 )
                        valid = false;
                    else {
                        init [bi] [bj] = init [bi + 1] [bj];
                        init [bi + 1] [bj] = ' ';
                        bi++;
                    }
                    break;

                case 'R' :
                    if ( bj == 4 )
                        valid = false;
                    else {
                        init [bi] [bj] = init [bi] [bj + 1];
                        init [bi] [bj + 1] = ' ';
                        bj++;
                    }
                    break;

                case 'L' :
                    if ( bj == 0 )
                        valid = false;
                    else {
                        init [bi] [bj] = init [bi] [bj - 1];
                        init [bi] [bj - 1] = ' ';
                        bj--;
                    }
                    break;
                }
            }
        }

        if ( line )
            printf ("\n");
        line = true;

        printf ("Puzzle #%d:\n", count++);

        if ( valid ) {
            for ( int i = 0; i < 5; i++ ) {
                printf ("%c %c %c %c %c\n", init [i] [0], init [i] [1],
                        init [i] [2], init [i] [3], init [i] [4]);
            }
        }

        else
            printf ("This puzzle has no final configuration.\n");
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容