紫书第4章 函数和递归

1  序

       系统的整理下第四章的学习笔记。同上次一样,尽量在不依赖书本的情况下自己先把例题做出来。这次有许多道题代码量都比较大,在例题中我都用纯C语言编写,但由于习题的挑战性和复杂度,我最终还是决定在第五章开始前,就用C++来完成习题。不过所有的代码都是能在C++提交下AC的。
       在习题中,我都习惯性的构造一个类来求解问题,从我个人角度讲,这会让我的思路清晰不少,希望自己的一些代码风格不会影响读者对解题思路的理解。
       其实在第四章前,我就顾虑着是不是真的打算把题目全做了,这些题目代码量这么大,要耗费很长一段时间。事实证明我多虑了,一分付出一分收获,经过两周的练习,我的编码能力进步很大。以前比赛一直很怕复杂麻烦的模拟题,现在遇到模拟题反而很开心,因为我算法题会的很少啊~~~
       我是从前往后花了两周把题目做一遍的,然后又从后往前整理出每道题的题解,再整理出博客。一些东西弄着弄着可能会没耐心了或心烦,忽悠了事,所以写的不好、有错的地方读者尽管指出,我会修改完善。
       建议配合紫书——《算法竞赛入门经典(第2版)》阅读本博客,转载请注明出处: code4101,谢谢。

2  例题

2.1  UVa1339--Ancient Cipher

       思路同书本。只要将两个字符串s1、s2的26个字母分别出现的次数统计出来,存在int a[26]与b[26],然后a、b数组按数值从小到大排序后,看a、b是否相等就能知道能否一一映射。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int cmp(const void* a,const void* b) {
    return *(int *)a - *(int *)b;
}

int main() {
    char s1[110], s2[110];

    while (scanf("%s%s", s1, s2) != EOF) {
        int a[26] = {}, b[26] = {}, i;
        for (i = 0; s1[i]; i++) a[s1[i]-'A']++;
        for (i = 0; s2[i]; i++) b[s2[i]-'A']++;
        qsort(a, 26, sizeof(int), cmp);
        qsort(b, 26, sizeof(int), cmp);
        printf("%s\n", memcmp(a, b, sizeof(a))?"NO":"YES");
    }
    return 0;
}

2.2  UVa489--Hangman Judge

       这题小白书做过,我是用标记数组,来判断哪些字母是否有访问过来求解的。
#include <stdio.h>

int main()
{
	int t, i, c;
	char s[1000];
	bool vis[26], has[26];
	
	while (scanf("%d ", &t), t != -1)
	{
		for (i = 0; i < 26; i++) vis[i] = has[i] = 0;
		scanf("%s", s);
		
		for (i = 0; s[i]; i++) has[s[i]-'a'] = 1;
		for (i = 0; i < 26; i++) vis[i] = has[i];
		
		scanf("%s", s);
		for (c = i = 0; s[i]; i++)
		{
			if (has[s[i]-'a']) vis[s[i]-'a'] = 0;
			else c++;
			
			if (c == 7) break;
		}
		for (i = 0; i < 26; i++) if (vis[i]) break;
		
		printf("Round %d\n", t);
		if (i == 26 && c <= 7) printf("You win.\n");
		else if (i < 26 && c < 7) printf("You chickened out.\n");
		else printf("You lose.\n");
	}
	return 0;
}

2.3  UVa133--The Dole Queue

       一开始的思路不太好,我想用一个数组,每有1个或2个离队,就把其值对应删掉(用数组平移,n递减来实现),导致条件分支太多。
       其实这题数据非常小,离队的用0值来标记,就不会变的这么麻烦了。
       代码是模仿书上的。
#include <stdio.h>
int n, k, m, a[25];

int go(int p, int d, int t) {
    while (t--) {
        do {
            p = (p+d+n-1) % n + 1;
        } while (a[p] == 0);
    }
    return p;
}

int main() {
    while (scanf("%d%d%d", &n, &k, &m), n&&k&&m) {
        for (int i = 1; i <= n; i++) a[i] = i;
        int left = n;
        int p1 = n, p2 =1;
        while (left) {
            p1 = go(p1, 1, k);
            p2 = go(p2, -1, m);
            printf("%3d", p1); left--;
            if (p2 != p1) { printf("%3d", p2); left--;}
            a[p1] = a[p2] = 0;
            if (left) printf(",");
        }
        printf("\n");
    }
    return 0;
}

2.4  UVa213--Message Decoding

       我先从LRJ讲的思路和框架入手,然后在根据自己的习惯优化代码。
       题目说了编码头(header)只占一行,所以没必要像LRJ的readcodes那么复杂,我直接用my_gets读入header字符串s,然后用init函数与s来初始化code。
#include <stdio.h>
#include <string.h>
char code[8][1<<8], s[1<<8];

// 过滤空行,返回字符串长度
int my_gets(char* s) {
    int k;
    while ((k = getchar()) == '\n');  // 空行重读
    if (k == -1) k = 0; else gets(s+1);
    s[0] = k; return strlen(s);
}

void init() {
    int i, j, k = 0;
    for (i = 1; ; i++) {
        for (j = 0; j < (1<<i)-1; j++) {
            code[i][j] = s[k++];
            if (!s[k]) return;
        }
    }
}

// 读取01字符,返回0、1整型值
int read() {
    char ch;
    while ((ch = getchar()) == '\n');
    return ch - '0';
}

// 读取c个01字符
int readint(int c) {
    int v = 0;
    while (c--) v = (v<<1)+read();
    return v;
}

int main() {
    int len, v;
    while (my_gets(s)) {
        init();
        while (len = readint(3))
            while ((v = readint(len)) != ((1<<len)-1))
                putchar(code[len][v]);
        putchar('\n');
    }
    return 0;
}

2.5  UVa512--Spreadsheet Tracking

       定义D[51][51][2],D[i][j][0]代表初始位置在第i行第j的元素当前在的行数,D[i][j][1]代表所在列。
       其他操作都好写,就是EX的交换操作挂了。WA 5次全是错在交换这里。其实这题自己想出的解题方法还好,主要不是思路不合适的原因。
       主要问题就是自己没有处理好查找问题,没想过:可不可能找不到?边查边修改值也是兵家之大忌,迫不得已这样操作时要万万小心。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FOR() for (int i = 1; i <= R; i++) for (int j = 1; j <= C; j++)
int R, C, D[51][51][2];

int cmp(const void* a, const void* b) {return *(int *)a-*(int *)b;}
void init() { FOR() D[i][j][0] = i, D[i][j][1] = j; }

void EX() {
    int r1, c1, r2, c2;
    scanf("%d%d%d%d", &r1, &c1, &r2, &c2);
    FOR() if (D[i][j][0]==r1&&D[i][j][1]==c1) {D[i][j][0] = r2; D[i][j][1] = c2;}
     else if (D[i][j][0]==r2&&D[i][j][1]==c2) {D[i][j][0] = r1; D[i][j][1] = c1;}
}

void DR(int r) {
    FOR() {
        if (D[i][j][0] == r) D[i][j][0] = 0;
        else if (D[i][j][0] > r) D[i][j][0]--;
    }
}
void DC(int c) {
    FOR() {
        if (D[i][j][1] == c) D[i][j][0] = 0;
        else if (D[i][j][1] > c) D[i][j][1]--;
    }
}
void IR(int r) { FOR() if (D[i][j][0] >= r) D[i][j][0]++; }
void IC(int c) { FOR() if (D[i][j][1] >= c) D[i][j][1]++; }

int main() {
    int kase, cnt, i, j, r, c, a[15], k;
    char cmd[5];

    for (kase = 1; scanf("%d%d",&R,&C), R; kase++) {
        init(); scanf("%d", &cnt);
        for (i = 0; i < cnt; i++) {
            scanf("%s", cmd);
            if (!strcmp(cmd,"EX")) EX();
            else {
                scanf("%d", &k);
                for (j = 0; j < k; j++) scanf("%d", a+j);
                qsort(a, k, sizeof(int), cmp);

                int dt = 1;
                void (*pf)(int);    // 函数指针
                if (!strcmp(cmd,"DR")) pf = DR, dt = -1;
                else if (!strcmp(cmd,"DC")) pf = DC, dt = -1;
                else if (!strcmp(cmd,"IR")) pf = IR;
                else pf = IC;
                for (j = 0; j < k; j++) pf(a[j]+dt*j);
            }
        }

        scanf("%d", &cnt);
        if (ka
  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值