【操作指导 | 代码实现】挑战程序设计竞赛2:算法和数据结构

书籍封面

第一章 前言

1. 本人衷心建议

       ~~~~~~        如果你是一位初学者,我指的是你只会基本的 C/C++ 编程,即使编的很烂,这本书对于你算法和数据结构的提升非常有帮助,所涉及的每一个算法案例都非常经典,且有非常细致的讲解,本人强烈推荐你理解好每一道题的细节。

       ~~~~~~        如果你具备一定的算法与数据结构能力,本书的内容依然很适合你,你可以有针对性的补充相应章节的内容,一定会有意想不到的收获。

       ~~~~~~        当你看到如此多的章节,每个章节还有如此多的内容时,一定不要被吓到,当本人对算法与数据结构一头雾水时,也是一步步算法过来的,这本书的学习大概花费了我近一个月的时间,所以你不必想着马上就可以看完很多,知识需要慢慢消化,Good luck !

注:算法涉及的题目如何在网上搜索见下文,特意强调,无需翻墙。

2. 本书涉及的所有题目以及对应的编号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. AOJ 使用方法《挑战程序设计竞赛》

网址

https://onlinejudge.u-aizu.ac.jp/home

注册

点右上角 Guest ==> Sign Up

注册完成后点击 submit

查找题目

1.点击网站上方 Challenge ==> Search

2.直接搜索题目

3. 或者根据上面 2 中题目对应的编号进行查找

点击 Volumes ,因为题号是 0202,前两位是 02,所以选 Volume2,然后找到对应编号即可

做题

进去之后选择语言,编写代码,并提交即可。当你看到全是日语的时候,不要惊慌,你完全可以看懂题,毕竟每道题的中文翻译都在本文中了…

提交后往下翻查看状态
在这里插入图片描述

用 virtual judge 做 AOJ 的题目

一个非常便捷的网站:https://vjudge.net/problem#OJId=Aizu&probNum=&title=&source=&category=all
首先进行账号注册


点进去做题就可以了

第二章 算法与复杂度

2.4 算法的效率

在这里插入图片描述

2.4.1 复杂度的评估

在这里插入图片描述

2.4.2 大 O 表示法

在这里插入图片描述
在这里插入图片描述

2.4.3 复杂度的比较

在这里插入图片描述

第三章 初等排序

3.1 挑战问题之前——排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 插入排序法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

// 打印数列A
void print(int A[],int n) {
    for (int i = 0; i < n; i++)
        printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
}
// 插入排序法
void insertsort(int A[], int n) {
    // 从第二个开始,往前找比当前的小的值,
    // 比当前值大的就往后移,知道找到小于等于A[i]的
    for (int i = 1; i < n; i++) {
        int j = i - 1;
        int v = A[i];
        while (j >= 0 && A[j] > v) {
            A[j + 1] = A[j];
            j--;
        }
        A[j + 1] = v;
        print(A, n);
    }
}
int main() {
    int n, A[110];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    print(A, n);
    insertsort(A, n);
    return 0;
}

3.3 冒泡排序法

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

// 冒泡排序法
int bubbleSort(int A[], int n) {
    int cnt = 0;
    // 标记是否需要继续冒泡
    int flag = 1;
    for (int i = 0; flag; i++) {
        flag = 0;
        for (int j = n - 1; j >= i + 1; j--) {
            // 交换两个相邻的元素
            if (A[j] < A[j - 1]) {
                swap(A[j], A[j - 1]);
                flag = 1;
                cnt++;
            }
        }
    }
    return cnt;
}
int main() {
    int n, A[110];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);

    int cnt = bubbleSort(A, n);
    for (int i = 0; i < n; i++)
        printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
    printf("%d\n", cnt);
    return 0;
}

3.4 选择排序法

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

// 选择排序法
int selectionSort(int A[], int n) {
    int cnt = 0;

    for (int i = 0; i < n; i++) {
        int minj = i;
        for (int j = i; j < n; j++) {
            if (A[j] < A[minj])
                minj = j;
        }
        if (i != minj)
            swap(A[i], A[minj]), cnt++;
    }
    return cnt;
}
int main() {
    int n, A[110];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);

    int cnt = selectionSort(A, n);
    for (int i = 0; i < n; i++)
        printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
    printf("%d\n", cnt);
    return 0;
}

3.6 希尔排序法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;

//g数组
vector<int>G;
long long cnt;
int A[1000000 + 10];

// 间隔为g的插入排序
void insertionSort(int A[], int n, int g) {
    for (int i = g; i < n; i++) {
        int v = A[i];
        int j = i - g;
        while (j >= 0 && A[j] > v) {
            A[j + g] = A[j];
            j -= g;
            cnt++;
        }
        A[j + g] = v;
    }
}
//希尔排序
void shellsort(int A[], int n) {
    for (int i = G.size() - 1; i >= 0; i--) {
        insertionSort(A, n, G[i]);
    }
}

// 生成g数组
void getArray(int n) {
    for (int h = 1; h <= n;) {
        G.push_back(h);
        h = 3 * h + 1;
    }
}


int main() {

    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    getArray(n);
    shellsort(A,n);

    int len = G.size();
    printf("%d\n", len);
    for (int i = len - 1; i >= 0; i--)
        printf("%d%c", G[i], i == 0 ? '\n' : ' ');
    printf("%lld\n", cnt);
    for (int i = 0; i < n; i++)
        printf("%d\n", A[i]);
    return 0;
}

第四章 数据结构

4.1 挑战问题之前一一什么是数据结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 栈

在这里插入图片描述

在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

stack1

/*实现栈的功能*/
#include<iostream>
#include<cstdio>
using namespace std;

// 实现栈的功能

// top是指向栈顶的指针, s是实现栈结构的数组
int top, S[1000];

// 将x压入栈的操作
void push(int x) {
    // top加1之后将元素插入到top所指的位置
    S[++top] = x;
}

// 将栈顶元素返回并删除
int pop() {
    top--;
    return S[top + 1];
}

// 字符转数字
int CharToInt(char s[]) {
    int ans = 0, i = 0;
    while (s[i] != '\0') {
        ans = ans * 10 + s[i] - '0';
        i++;
    }
    return ans;
}
int main() {
    char s[1000];
    int a, b;
    // 清空栈
    top = 0; 
    while(scanf("%s",s)!=EOF){
            if (s[0] == '+') {
                b = pop();a = pop();
                push(a + b);
            }else if (s[0] == '-') {
                b = pop(); a = pop();
                push(a - b);
            }else if (s[0] == '*') {
                b = pop(); a = pop();
                push(a * b);
            }else {
                push(CharToInt(s));
            }
    }
    printf("%d\n",pop());

    return 0;
}

stack2

/*利用STL的stack类*/
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;

// 字符转数字
int CharToInt(char s[]) {
    int ans = 0, i = 0;
    while (s[i] != '\0') {
        ans = ans * 10 + s[i] - '0';
        i++;
    }
    return ans;
}

int main() 
{
    char s[1000];
    int a, b;
    stack<int> S;

    while(scanf("%s",s)!=EOF){
            if (s[0] == '+') {
                b = S.top(); S.pop();
                a = S.top(); S.pop();
                S.push(a + b);
            }else if (s[0] == '-') {
                b = S.top(); S.pop();
                a = S.top(); S.pop();
                S.push(a - b);
            }else if (s[0] == '*') {
                b = S.top(); S.pop();
                a = S.top(); S.pop();
                S.push(a * b);
            }else {
                S.push(CharToInt(s));
            }
    }
    printf("%d\n", S.top());

    return 0;
}

4.3 队列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

queue1

/*实现队列功能*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxx = 100000 + 10;
struct task {
    // 任务的名字和耗费的时间
    char name[100];
    int t;
};
task Queue[maxx];

// 指向队头和队尾的指针
int head, tail;


// 判断循环队列是否已满
bool isFull() {
    return head == (tail + 1) % maxx;
}

// 判断循环队列是否为空
bool isEmpty() {
    return head == tail;
}

// 将元素x添加到队列的末尾
void enqueue(task x) {
    Queue[tail] = x;
    // 循环队列
    tail = (tail + 1) % maxx;
}

// 返回队头元素并删除
task dequeue() {
    task x = Queue[head];
    // 循环队列
    head = (head + 1) % maxx;
    return x;
}
int main() {
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        scanf("%s %d", Queue[i].name, &Queue[i].t);
    }
    // 当前已经有n个元素
    head = 0; tail = n;
    // 当前时间
    int nowtime = 0;
    while (!isEmpty()) {
        // 取出队头
        task u = dequeue();
        // 执行时间片 p 和 所需要时间u.t
        int c = min(q, u.t);
        // 任务执行了 c ms
        u.t -= c;
        // 当前时间加上c ms
        nowtime += c;
        // 任务还没做完,加入到队尾
        if (u.t > 0) {
            enqueue(u);
        }
        else { // 任务已经完成,打印出结果
            printf("%s %d\n", u.name, nowtime);
        }

    }
    return 0;
}

queue2

/*利用STL的queue类*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

const int maxx = 100000 + 10;
struct task {
    // 任务的名字和耗费的时间
    char name[100];
    int t;
};
queue<task> Queue;

int main() {
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        task t;
        scanf("%s %d", t.name, &t.t);
        Queue.push(t);
    }
    // 当前时间
    int nowtime = 0;
    while (!Queue.empty()) {
        // 取出队头
        task u = Queue.front();
        Queue.pop();
        // 执行时间片 p 和 所需要时间u.t
        int c = min(q, u.t);
        // 任务执行了 c ms
        u.t -= c;
        // 当前时间加上c ms
        nowtime += c;
        // 任务还没做完,加入到队尾
        if (u.t > 0) {
            Queue.push(u);
        }
        else { // 任务已经完成,打印出结果
            printf("%s %d\n", u.name, nowtime);
        }

    }
    return 0;
}

4.4 链 表

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

List1

/*实现双向链表功能*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

const int maxx = 100000 + 10;
struct Node {
    int key;
    Node *prev, *next;
};

Node *nil;

// 链表初始化
void init() {
    nil = (Node *)malloc(sizeof(Node));
    //C++ nil = new Node();
    nil->next = nil;
    nil->prev = nil;
}

// 从链表搜索出值为key的结点 O(n)
Node* listSearch(int key) {
    // 从链表头结点开始访问
    Node *cur = nil->next;
    while (cur != nil && cur->key != key) {
        cur = cur->next;
    }
    return cur;
}

// 打印链表
void printList() {
    Node *cur = nil->next;
    int flag = 0;
    while (cur != nil) {
        if (flag++ > 0)
            printf(" ");
        printf("%d", cur->key);
        cur = cur->next;
    }
    printf("\n");
}

// 删除结点t
void deleteNode(Node *t) {
    // t为头结点不做处理
    if (t == nil)
        return;
    t->prev->next = t->next;
    t->next->prev = t->prev;
    free(t);
    //C++ delete(t);
}

// 删除头结点
void deleteFirst() {
    deleteNode(nil->next);
}

// 删除尾结点
void deleteLast() {
    deleteNode(nil->prev);
}

// 删除指定的key O(n)
void deleteKey(int key) {
    deleteNode(listSearch(key));
}

// 在链表头部添加元素key
void insert(int key) {
    Node *x = (Node *)malloc(sizeof(Node));
    //C++ Node *x = new Node();
    x->key = key;

    x->next = nil->next;
    nil->next->prev = x;
    nil->next = x;
    x->prev = nil;
}
int main() {
    int n, key, size = 0;
    char op[20];
    scanf("%d", &n);
    getchar();
    // 初始化链表
    init();
    for (int i = 0; i < n; i++) {
        scanf("%s %d", op, &key);
        if (op[0] == 'i') {
            insert(key);
            size++;
        }
        else {
            if (strlen(op) > 6) {
                if (op[6] == 'F') {//删除头结点
                    deleteFirst();
                }
                else {//删除尾结点
                    deleteLast();
                }
            }
            else { //删除结点
                deleteKey(key);
            }
            size--;
        }
    }
    printList();
    return 0;
}

List2

/*利用STL的list类*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<list>
using namespace std;

int main() {
    int n, key;
    char op[20];
    list<int> List;
    scanf("%d", &n);
    getchar();

    for (int i = 0; i < n; i++) {
        scanf("%s %d", op, &key);
        if (op[0] == 'i') {
            List.push_front(key);
        }
        else {
            if (strlen(op) > 6) {
                if (op[6] == 'F') {//删除头结点
                    List.pop_front();
                }
                else {//删除尾结点
                    List.pop_back();
                }
            }
            else { //删除结点
                for (list<int>::iterator it = List.begin(); it != List.end(); it++) {
                    if (*it == key) {
                        List.erase(it);
                        break;
                    }
                }
            }
        }
    }
    int flag = 0;
    for (list<int>::iterator it = List.begin(); it != List.end(); it++) {
        if (flag != 0)
            printf(" ");
        flag++;
        printf("%d", (*it));
    }
    printf("\n");
    return 0;
}

4.5 标准库的数据结构

在这里插入图片描述
在这里插入图片描述

4.5.2 stack

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5.3 queue

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.5.4 vector

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5.5 list

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第五章 搜索

5.1 挑战问题之前一搜索

在这里插入图片描述
在这里插入图片描述

5.2 线性搜索

在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
using namespace std;

// 线性搜索
bool LinearSearch(int A[], int n, int key) {
    // 将key设置为数列最后一项
    A[n] = key;
    int i = 0;
    while (A[i] != key)
        i++;
    // 如果i不是n,则代表在原来的数列中找到key,
    // 否则代表原来数列中没有key,找到的是刚才插入到最后的元素
    return i != n;
}
int main() {
    int n, q, key;
    int sum = 0;
    int A[10000 + 10];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d", &key);
        if (LinearSearch(A, n, key))
            sum++;
    }
    printf("%d\n", sum);
}

5.3 二分搜索

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

方式一

/*实现二分搜索*/
#include<iostream>
#include<cstdio>
using namespace std;

// 二分搜索
bool BinarySearch(int A[], int n, int key) {
    int left = 0 , right = n, mid;

    while (left < right) {
        mid = (left + right) / 2;
        if (key == A[mid])
            return true;

        if(key > A[mid])
            left = mid + 1;
        if (key < A[mid])
            right = mid;
    }
    return false;

}
int main() {
    int n, q, key;
    int sum = 0;
    int A[100000 + 10];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d", &key);
        if (BinarySearch(A, n, key))
            sum++;
    }
    printf("%d\n", sum);
}

方式二

/*利用STL的binary_search*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
    int n, q, key;
    int sum = 0;
    int A[100000 + 10];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d", &key);
        if (binary_search(A, A + n, key))
            sum++;
    }
    printf("%d\n", sum);
}

5.4 散列法

在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const LL maxx = 1000000 + 10;
char H[maxx][15];

// 将字符转换为数值
LL CharToInt(char ch) {
    if (ch == 'A') return 1;
    if (ch == 'C') return 2;
    if (ch == 'G') return 3;
    if (ch == 'T') return 4;
    return 0;
}

// 将字符串转成数值并生成key
long long getKey(char str[]) {
    LL sum = 0, p = 1;
    LL len = strlen(str);
    for (long long int i = 0; i < len; i++) {
        sum += p * CharToInt(str[i]);
        p *= 5;
    }
    return sum;
}

LL h1(LL key) {
    return key % maxx;
}
LL h2(LL key) {
    return 1 + (key % (maxx - 1));
}
// find 操作
bool find(char str[]) {
    LL key, h;
    key = getKey(str);
    for (LL i = 0;; i++) {
        h = (h1(key) + i * h2(key)) % maxx;
        //找到key
        if (strcmp(H[h], str) == 0) return true;
        // key不存在
        else if (strlen(H[h]) == 0) return false;
    }
    return false;

}
// insert操作
void insert(char str[]) {
    LL key, h;
    key = getKey(str);
    for (LL i = 0;; i++) {
        h = (h1(key) + i * h2(key)) % maxx;
        //找到key
        if (strcmp(H[h], str) == 0) return ;
        // key不存在
        else if (strlen(H[h]) == 0) {
            // 插入
            strcpy(H[h], str);
            return ;
        }
    }
}
int main() {
    int n, q, key;
    char str[20], op[20];
    for (int i = 0; i < maxx; i++)
        H[i][0] = '\0';
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s %s", op, str);
        if (op[0] == 'i')
            insert(str);
        else {
            if (find(str))
                printf("yes\n");
            else
                printf("no\n");
        }
    }
    return 0;
}

5.5 借助标准库搜索

5.5.1 迭 代 器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.5.2 lower bound

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.6 搜索的应用——计算最优解

这是一个稍微有些难度的挑战题。如果各位觉得太难可以暂时跳过,等具备一定实力后再冋过头来挑战。
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL A[100000 + 10];
int n, k;
//模拟装货过程
bool check(LL P) {
    int cnt = k;
    for (int i = 0; i < n; ) {
        LL t = P;
        cnt--;
        // 卡车已经用完,证明卡车当前最大装载量不能装完货物
        if (cnt < 0)
            return false;
        while (i < n && t >= 0) {
            // 剩余重量可以装上当前货物
            if (t >= A[i])
                t -= A[i],i++;
            //装不下则用下一辆卡车去装
            if (t < A[i])
                break;
        }
    }
    return true;
}
// 二分找出P的最小值
LL BinarySearch(LL sum) {
    LL left = 0, right = sum;
    LL mid;
    while (left + 1 < right) {
        mid = (left + right) / 2;
        //可以装好货物
        if (check(mid))
            right = mid;
        else
            left = mid;
    }

    return right;
}
int main() {
    LL sum = 0;
    scanf("%d %d", &n, &k);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &A[i]);
        sum += A[i];
    }
    LL P = BinarySearch(sum);
    printf("%lld\n", P);
    return 0;
}

第六章 递归和分治法

6.1 挑战问题之前一递归与分治

在这里插入图片描述
在这里插入图片描述

6.2 穷 举 搜 索

在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
using namespace std;

int n, A[30];

// 递归穷举
bool solve(int i, int m) {
    // 已经找到得到m的组合方式
    if (m == 0)
        return true;
    // 已经穷举过A的全部元素了
    if (i >= n)
        return false;
    // 分别穷举 不选A[i] 和 选择A[i]的情况
    return solve(i + 1, m) || solve(i + 1, m - A[i]);
}
int main(){
    int t, M;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &A[i]);
    }
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        scanf("%d", &M);
        if (solve(0, M))
            printf("yes\n");
        else
            printf("no\n");
    }
    return 0;
}

6.3 科 赫 曲 线

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const double PI = acos(-1.0);
struct Point{
    double x,y;
};

void koch(int n, Point a, Point b) {
    // 递归结束
    if (n == 0)
        return;
    // 三等分点s,t ,(u 是 s,t 上方的点)
    Point s, t, u;
    // 角度从度转成弧度
    double th = PI * 60.0 / 180.0;
    s.x = (2.0 * a.x + 1.0 * b.x) / 3.0;
    s.y = (2.0 * a.y + 1.0 * b.y) / 3.0;
    t.x = (1.0 * a.x + 2.0 * b.x) / 3.0;
    t.y = (1.0 * a.y + 2.0 * b.y) / 3.0;
    u.x = (t.x - s.x) * cos(th) - (t.y - s.y) * sin(th) + s.x;
    u.y = (t.x - s.x) * sin(th) + (t.y - s.y) * cos(th) + s.y;


    koch(n - 1, a, s);
    printf("%.8lf %.8lf\n", s.x, s.y);
    koch(n - 1, s, u);
    printf("%.8lf %.8lf\n", u.x, u.y);
    koch(n - 1, u, t);
    printf("%.8lf %.8lf\n", t.x, t.y);
    koch(n - 1, t, b);
}
int main(){
    Point a, b;
    int n;
    scanf("%d", &n);

    // 初始化
    a.x = a.y = 0;
    b.x = 100, b.y = 0;
    printf("%.8lf %.8lf\n", a.x, a.y);
    koch(n, a, b);
    printf("%.8lf %.8lf\n", b.x, b.y);
    return 0;
}

第八章 树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2 有根树的表达

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;


struct Node {
    int p, l, r;
};
Node Tree[MAX];
int n, Depth[MAX];


void print(int u) {
    printf("node %d: ", u);
    printf("parent = %d, ", Tree[u].p);
    printf("depth = %d, ", Depth[u]);

    if (Tree[u].p == NIL)
        printf("root, ");
    else if (Tree[u].l == NIL)
        printf("leaf, ");
    else
        printf("internal node, ");

    printf("[");
    // 从子结点的最左边开始遍历
    int c = Tree[u].l;
    while (c != NIL) {
        printf("%d", c);
        if (Tree[c].r != NIL)
            printf(", ");
        // 跳到右侧紧邻的兄弟结点
        c = Tree[c].r;
    }
    printf("]\n");
}

// 递归得求深度
void rec(int u, int depth) {
    Depth[u] = depth;
    if (Tree[u].r != NIL)  // 右侧兄弟设置为相同深度
        rec(Tree[u].r, depth);
    if (Tree[u].l != NIL)  // 最左侧子结点的深度设置为自己的深度 + 1
        rec(Tree[u].l, depth + 1);
}
int main() {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        Tree[i].p = Tree[i].l = Tree[i].r = NIL;
    }
    int v, num, c, l, root;
    for (int i = 0; i < n; i++) {
        // v是当前结点值 ,num是v结点的子结点数 
        scanf("%d %d", &v, &num);
        for (int j = 0; j < num; j++) {
            scanf("%d", &c);

            //设置最左侧结点 和 紧邻右侧兄弟结点
            // l记录的是上一个结点,将当前的结点设置为l的紧邻右侧结点
            if (j == 0)
                Tree[v].l = c;
            else
                Tree[l].r = c;
            l = c;
            // 设置父结点
            Tree[c].p = v;
        }
    }
    // 找出根结点
    for (int i = 0; i < n; i++) {
        if (Tree[i].p == NIL)
            root = i;
    }

    rec(root, 0);
    for (int i = 0; i < n; i++)
        print(i);
    return 0;
}

8.3 二叉树的表达

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;


struct Node {
    int parent, left, right;
};
Node Tree[MAX];
int n, Depth[MAX], Height[MAX];

int getDegree(int u) {
    int deg = 0;
    if (Tree[u].left != NIL)
        deg++;
    if (Tree[u].right != NIL)
        deg++;
    return deg;
}
void setDepth(int u, int depth) {
    if (u == NIL)
        return;
    Depth[u] = depth;
    setDepth(Tree[u].left, depth + 1);
    setDepth(Tree[u].right, depth + 1);
}

int setHeight(int u) {
    int h1 = 0, h2 = 0;
    if (Tree[u].left != NIL)
        h1 = setHeight(Tree[u].left) + 1;
    if (Tree[u].right != NIL)
        h2 = setHeight(Tree[u].right) + 1;
    return Height[u] = max(h1, h2);
}

int getSibling(int u) {
    if (Tree[u].parent == NIL)
        return NIL;
    //存在左兄弟
    if (Tree[Tree[u].parent].left != u && Tree[Tree[u].parent].left != NIL)
        return Tree[Tree[u].parent].left;
    //存在右兄弟
    if (Tree[Tree[u].parent].right != u && Tree[Tree[u].parent].right != NIL)
        return Tree[Tree[u].parent].right;
    return NIL;
}

void print(int u) {
    printf("node %d: parent = %d, sibling = %d, degree = %d, depth = %d, height = %d, "
        , u, Tree[u].parent, getSibling(u), getDegree(u), Depth[u], Height[u]);

    if (Tree[u].parent == NIL)
        printf("root\n");
    else if (Tree[u].left != NIL || Tree[u].right != NIL)
        printf("internal node\n");
    else
        printf("leaf\n");
}

int main() {
    int n;
    int u, l, r, root;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        Tree[i].parent = Tree[i].left = Tree[i].right = NIL;
    }

    for (int i = 0; i < n; i++) {
        scanf("%d %d %d", &u, &l, &r);
        Tree[u].left = l;
        Tree[u].right = r;
        Tree[l].parent = u;
        Tree[r].parent = u;
    }
    for (int i = 0; i < n; i++) {
        if (Tree[i].parent == NIL)
            root = i;
    }
    setHeight(root);
    setDepth(root, 0);
    for (int i = 0; i < n; i++)
        print(i);
    return 0;
}

8.4 树的遍历

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
前序遍历:F, B, A, D, C, E, G, I, H
中序遍历:A, B, C, D, E, F, G, H, I
后续遍历:A, C, E, D, B, H, I, G, F
层序遍历:F, B, G, A, D, I, C, E, H

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;


struct Node {
    int parent, left, right;
};
Node Tree[MAX];
int n;
//前序排序
void PreorderTreeWalk(int u) {
    if (u == NIL)
        return;
    printf(" %d", u);
    PreorderTreeWalk(Tree[u].left);
    PreorderTreeWalk(Tree[u].right);
}
//中序排序
void InorderTreeWalk(int u) {
    if (u == NIL)
        return;
    InorderTreeWalk(Tree[u].left);
    printf(" %d", u);
    InorderTreeWalk(Tree[u].right);
}
//后序排序
void PostorderTreeWalk(int u) {
    if (u == NIL)
        return;
    PostorderTreeWalk(Tree[u].left);
    PostorderTreeWalk(Tree[u].right);
    printf(" %d", u);
}


int main() {
    int n;
    int u, l, r, root;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        Tree[i].parent = Tree[i].left = Tree[i].right = NIL;
    }

    for (int i = 0; i < n; i++) {
        scanf("%d %d %d", &u, &l, &r);
        Tree[u].left = l;
        Tree[u].right = r;
        Tree[l].parent = u;
        Tree[r].parent = u;
    }
    for (int i = 0; i < n; i++) {
        if (Tree[i].parent == NIL)
            root = i;
    }
    printf("Preorder\n");
    PreorderTreeWalk(root);
    printf("\nInorder\n");
    InorderTreeWalk(root);
    printf("\nPostorder\n");
    PostorderTreeWalk(root);
    printf("\n");
    return 0;
}

8.5 树遍历的应用 —— 树的重建

在这里插入图片描述
在这里插入图片描述

题解

图中后序遍历的结果为:3 5 6 4 2 8 9 7 1

在这里插入图片描述
在这里插入图片描述

代码实现

1、已知前序遍历和中序遍历,求后续遍历。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
vector<int> pre, in, post;
int pos;
int n, k;

void reconstruction(int left, int right) {
    // 遍历到叶结点
    if (left >= right)
        return;

    // 当前子树的根
    int root = pre[pos++];
    // 找出当前根的值在中序数组里的下标
    int m = distance(in.begin(), find(in.begin(), in.end(), root));

    reconstruction(left, m);
    reconstruction(m + 1, right);

    //存入到后序数组
    post.push_back(root);

}
void solve() {
    pos = 0;
    reconstruction(0, pre.size());
    for (int i = 0; i < n; i++) {
        printf("%d%c", post[i], i == n-1?'\n':' ');
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &k);
        pre.push_back(k);
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &k);
        in.push_back(k);
    }
    solve();
    return 0;
}

2、已知后续遍历和中序遍历求前序遍历

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
vector<int> pre, in, post;
int pos;
int n, k;

void reconstruction(int left, int right) {
    // 遍历到叶结点
    if (left >= right)
        return;

    // 当前子树的根
    int root = pre[pos--];
    // 找出当前根的值在中序数组里的下标
    int m = distance(in.begin(), find(in.begin(), in.end(), root));

	reconstruction(m + 1, right); // 注意优先建立左边的树,根节点时从后往前的
    reconstruction(left, m);

    //存入到后序数组
    post.push_back(root);

}
void solve() {
    pos = n - 1;
    reconstruction(0, pre.size());
    for (int i = n - 1; i >= 0; i--) {
        printf("%d%c", post[i], i == n-1?'\n':' ');
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &k);
        pre.push_back(k);
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &k);
        in.push_back(k);
    }
    solve();
    return 0;
}

3、已知前序遍历和后序遍历不可求中序遍历
4、已知前序||中序||后序遍历(仅该树为完全二叉树),求层序遍历

#include<bits/stdc++.h>
using namespace std;
int n,a[100],tree[100];
int idx=1;
void f(int x)
{    
    if(x > n) return ;
    tree[x] = a[idx++];        //已知前序遍历,求层序遍历
    f(x << 1);
    //tree[x] = a[idx++];   //已知中序遍历,求层序遍历
    f((x << 1) + 1);
    //tree[x] = a[idx++];     //已知后序遍历,求层序遍历
}


int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
        scanf("%d",&a[i]);
    f(1);
    printf("层次遍历:");
    for(int i = 1; i <= n; i++)
        printf("%d ",tree[i]);
    return 0;
}

5、已知层序遍历,求前中后遍历(仅满足树为完全二叉树时)

#include<stdio.h>
const int N=100;
int pre[N],in[N],post[N],tree[N],idx;;
int n;


void f(int x)
{
    pre[idx++]=tree[x];        //求前序遍历
    if(x<<1<=n)
        f(x<<1);
    //in[idx++]=tree[x];    //求中序遍历
    if((x<<1)+1<=n)         //记得加括号,不然会先加1后乘2
        f((x<<1)+1);
    //post[idx++]=tree[x];    //求后序遍历
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)    //输入层遍历
        scanf("%d",&tree[i]);


    f(1);     


    for(int i=0;i<n;i++)    //输出
        printf("%d ",pre[i]);
    return 0;
}

第九章 二叉搜索树

在这里插入图片描述

9.2 二叉搜索树——插入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
    int key;
    Node *parent, *left, *right;
};
Node *root, *NIL;

//前序排序
void PreorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    printf(" %d", u->key);
    PreorderTreeWalk(u->left);
    PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    InorderTreeWalk(u->left);
    printf(" %d", u->key);
    InorderTreeWalk(u->right);
}


void insert(int value) {
    Node *y = NIL, *x = root;
	
	// 初始化 z 节点作为要插入的节点
    Node *z = new Node(); // Node *z = (Node *)malloc(sizeof(Node))
    z->key = value;
    z->left = z->right = NIL;

    // 从根结点往下遍历
    while (x != NIL) {
        y = x; // 记录搜索下去的前一个节点,作为 待插入节点 z 的父节点
        // z 比 x 小, 则从x的左侧遍历
        // z 比 x 大, 则从x的右侧遍历
        if (z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    z->parent = y;

    // 没有父结点的是root
    if (y == NIL) {
        root = z;
    }else {
        // z 比 y 小, 则在y的左侧
        // z 比 y 大, 则在y的右侧
        if (z->key < y->key)
            y->left = z;
        else
            y->right = z;
    }
}


int main() {
    int n, value;
    char s[200];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", s);
        if (s[0] == 'i') {
            scanf("%d", &value);
            insert(value);
        }else {
            InorderTreeWalk(root);
            printf("\n");
            PreorderTreeWalk(root);
            printf("\n");
        }
    }

    return 0;
}

9.3 二叉搜索树——搜索

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
    int key;
    Node *parent, *left, *right;
};
Node *root, *NIL;

//前序排序
void PreorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    printf(" %d", u->key);
    PreorderTreeWalk(u->left);
    PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    InorderTreeWalk(u->left);
    printf(" %d", u->key);
    InorderTreeWalk(u->right);
}


void insert(int value) {
    Node *y = NIL, *x = root;
    Node *z = new Node();
    z->key = value;
    z->left = z->right = NIL;

    // 从根结点往下遍历
    while (x != NIL) {
        y = x;
        // z 比 x 小, 则从x的左侧遍历
        // z 比 x 大, 则从x的右侧遍历
        if (z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    z->parent = y;

    // 没有父结点的是root
    if (y == NIL) {
        root = z;
    }else {
        // z 比 y 小, 则在y的左侧
        // z 比 y 大, 则在y的右侧
        if (z->key < y->key)
            y->left = z;
        else
            y->right = z;
    }
}

Node * find(Node *u, int value) {

    // 从根结点开始遍历下去
    while (u != NIL && u->key != value) {
        if (value < u->key)
            u = u->left;
        else
            u = u->right;
    }
    return u;
}

int main() {
    int n, value;
    Node * f;
    char s[200];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", s);
        if (s[0] == 'i') {
            scanf("%d", &value);
            insert(value);
        }
        else if (s[0] == 'f') {
            scanf("%d", &value);
            f = find(root,value);
            if (f != NIL)
                printf("yes\n");
            else
                printf("no\n");
        }else {
            InorderTreeWalk(root);
            printf("\n");
            PreorderTreeWalk(root);
            printf("\n");
        }
    }

    return 0;
}

9.4 二叉搜索树——删除

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
    int key;
    Node *parent, *left, *right;
};
Node *root, *NIL;

//前序排序
void PreorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    printf(" %d", u->key);
    PreorderTreeWalk(u->left);
    PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
    if (u == NIL)
        return;
    InorderTreeWalk(u->left);
    printf(" %d", u->key);
    InorderTreeWalk(u->right);
}


void insert(int value) {
    Node *y = NIL, *x = root;
    Node *z = new Node();
    z->key = value;
    z->left = z->right = NIL;

    // 从根结点往下遍历
    while (x != NIL) {
        y = x;
        // z 比 x 小, 则从x的左侧遍历
        // z 比 x 大, 则从x的右侧遍历
        if (z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    z->parent = y;

    // 没有父结点的是root
    if (y == NIL) {
        root = z;
    }
    else {
        // z 比 y 小, 则在y的左侧
        // z 比 y 大, 则在y的右侧
        if (z->key < y->key)
            y->left = z;
        else
            y->right = z;
    }
}
// 首先找到待删除值对应的节点,返回节点
Node * find(Node *u, int value) {

    // 从根结点开始遍历下去
    while (u != NIL && u->key != value) {
        if (value < u->key)
            u = u->left;
        else
            u = u->right;
    }
    return u;
}

// 树的最小值
Node *treeMinimum(Node * x) {
    while (x->left != NIL)
        x = x->left;
    return x;
}

// case 3 ,搜索后一个结点
Node *treeSuccessor(Node *x) {
    if (x->right != NIL)
        return treeMinimum(x->right);

    Node *y = x->parent;
    while (y != NIL && x == y->right) {
        x = y;
        x = y->parent;
    }
    return y;
}

// 传入的节点 z 即需要删除的节点
void treeDelete(Node * z) {
    Node *y, *x;  // 要删除的对象和 y 的子结点

    // 确定要删除的结点
    if (z->left == NIL || z->right == NIL)
        y = z;
    else
        y = treeSuccessor(z);

    // 确定 y 的子结点x
    if (y->left != NIL)
        x = y->left;
    else
        x = y->right;

    // 让子结点链接父结点
    if (x != NIL)
        x->parent = y->parent;

    // 让父结点链接子结点
    if (y->parent == NIL)
        root = x;
    else {
        if (y == y->parent->left)
            y->parent->left = x;
        else
            y->parent->right = x;
    }

    // case 3
    if (y != z)
        z->key = y->key;

    delete y;
}

int main() {
    int n, value;
    Node * f;
    char s[200];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", s);
        if (s[0] == 'i') {
            scanf("%d", &value);
            insert(value);
        }
        else if (s[0] == 'f') {
            scanf("%d", &value);
            f = find(root, value);
            if (f != NIL)
                printf("yes\n");
            else
                printf("no\n");
        }
        else if (s[0] == 'd') {
            scanf("%d", &value);
            treeDelete(find(root, value));
        }
        else {
            InorderTreeWalk(root);
            printf("\n");
            PreorderTreeWalk(root);
            printf("\n");
        }
    }

    return 0;
}

第十章 堆

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10.2 完全二叉树

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
using namespace std;

// 分别求出结点i的父结点,左子结点、右子结点
int parent(int i) { return i / 2; }
int left(int i) { return 2 * i; }
int right(int i) { return 2 * i + 1; }
int main()
{
    int H[300];
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &H[i]);

    for (int i = 1; i <= n; i++) {
        printf("node %d: key = %d, ", i, H[i]);
        if (parent(i) >= 1)
            printf("parent key = %d, ", H[parent(i)]);
        if (left(i) <= n) 
            printf("left key = %d, ", H[left(i)]);
        if (right(i) <= n)
            printf("right key = %d, ", H[right(i)]);
        printf("\n");
    }
    return 0;
}

10.3 最大/最小堆

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxx = 2000000;

int A[maxx + 1];
int n;
void maxHeapify(int i) {

    int left = 2 * i, right = 2 * i + 1;
    int largest;

    // 从左子结点、自身、右子结点选出值最大的结点,然后继续往下递归
    if (left <= n && A[left] > A[i])
        largest = left;
    else
        largest = i;

    if (right <= n && A[right] > A[largest])
        largest = right;

    if (largest != i) {
        swap(A[i], A[largest]);
        maxHeapify(largest);
    }

}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &A[i]);

    for (int i = n / 2; i >= 1; i--)
        maxHeapify(i);
    for (int i = 1; i <= n; i++)
        printf(" %d", A[i]);
    printf("\n");
    return 0;
}

10.4 优先级队列

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxx = 2000000;
const int INF = 1 << 30;

int A[maxx + 1];
int n;
void maxHeapify(int i) {

    int left = 2 * i, right = 2 * i + 1;
    int largest;

    // 从左子结点、自身、右子结点选出值最大的结点,然后继续往下递归
    if (left <= n && A[left] > A[i])
        largest = left;
    else
        largest = i;

    if (right <= n && A[right] > A[largest])
        largest = right;

    if (largest != i) {
        swap(A[i], A[largest]);
        maxHeapify(largest);
    }

}

int extract() 
{
    int maxv;
    if (n < 1)
        return -INF;
    maxv = A[1];
    A[1] = A[n--];
    maxHeapify(1);
    return maxv;
}

int insert(int key) {
    n++;
    int i = n;
    A[n] = -INF;
    if (key < A[i])
        return -INF;
    A[i] = key;
    while (i > 1 && A[i / 2] < A[i]) {
        swap(A[i], A[i / 2]);
        i /= 2;
    }

}
int main()
{
    char op[20];
    int k;
    while (scanf("%s", op) != EOF&&op[2] != 'd') {
        if (op[0] == 'i') {
            scanf("%d", &k);
            insert(k);
        }
        else {
            printf("%d\n", extract());
        }
    }
    return 0;
}

10.5 通过标准库实现优先级队列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第十一章 动态规划

11.1 动态规划法的概念

在这里插入图片描述
在这里插入图片描述

11.2 斐波那契数列

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
    int F[45],n;
    F[0] = F[1] = 1;
    for (int i = 2; i <= 44; i++)
        F[i] = F[i - 1] + F[i - 2];
    scanf("%d", &n);
    printf("%d\n", F[n]);
    return 0;

11.3 最长公共子序列

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxx = 1010;
int c[maxx][maxx];

int lcs(char s1[],char s2[]) {
    int m = strlen(s1), n = strlen(s2);
    for (int i = 1; i <= m; i++)
        c[i][0] = 0;
    for (int i = 1; i <= n; i++)
        c[0][i] = 0;

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (s1[i] == s2[j])
                c[i+1][j+1] = c[i][j] + 1;
            else
                c[i+1][j+1] = max(c[i][j+1], c[i + 1][j]);
        }
    }
    return c[m][n];
}
int main()
{
    char s1[maxx], s2[maxx];
    int t;
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        scanf("%s %s", s1, s2);
        printf("%d\n",lcs(s1,s2));
    }

    return 0;

11.4 矩阵链乘法

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxx = 110;
const int INF = 1 << 30;
int main()
{
    int n, p[maxx], m[maxx][maxx];

    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d %d", &p[i - 1], &p[i]);

    for (int i = 1; i <= n; i++)
        m[i][i] = 0;


    for (int l = 2; l <= n; l++) {
        for (int i = 1; i <= n - l + 1; i++) {
            int j = i + l - 1;
            m[i][j] = INF;
            for (int k = i; k <= j - 1; k++)
                m[i][j] = min(m[i][j], m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]);

        }
    }
    printf("%d\n", m[1][n]);
    return 0;
}

第十二章 图

12.1 挑战问题之前——图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.2 图的表示

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxx = 110;

int main()
{
    int n, m[maxx][maxx]; // 邻接矩阵
    int u, num, v;
    scanf("%d", &n);
    memset(m, 0, sizeof(m));
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", &u, &num);
        for (int j = 1; j <= num; j++) {
            scanf("%d", &v);
            m[u][v] = 1; // u 与 v 之间画一条边
        }
    }

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            printf("%d%c", m[i][j], j == n ? '\n' : ' ');

    return 0;
}

12.3 深度优先搜索

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int maxx = 110;
int n, m[maxx][maxx];
int u, num, v;
int d[maxx], f[maxx];
int flag[maxx];
int nowtime = 1;

void dfs(int u) {

    // 入栈时间
    flag[u] = 1;
    d[u] = nowtime++;
    for (int i = 1; i <= n; i++) {
        if (flag[i] == 0 && m[u][i] == 1) {
            dfs(i);
        }
    }
    //出栈时间
    f[u] = nowtime++;
}

int main()
{

    scanf("%d", &n);
    memset(m, 0, sizeof(m));
    memset(f, 0, sizeof(f));
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", &u, &num);
        for (int j = 1; j <= num; j++) {
            scanf("%d", &v);
            m[u][v] = 1;
        }
    }
    for (int i = 1; i <= n; i++) {
        if(flag[i] == 0)
            dfs(i);
    }
    for (int i = 1; i <= n; i++) {
        printf("%d %d %d\n", i, d[i], f[i]);
    }
    return 0;
}

12.4 广度优先搜索

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxx = 110;
int n, m[maxx][maxx];
int u, num, v;
int flag[maxx];
int d[maxx];


void bfs() {
    queue<int>q;

    q.push(1);
    d[1] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 2; i <= n; i++) {
            if (m[u][i] == 1 && flag[i] == 0) {
                // 标记为已经访问
                flag[i] = 1;
                // 更新长度
                d[i] = d[u] + 1;
                // 压入栈内
                q.push(i);
            }
        }
    }
}
int main()
{

    scanf("%d", &n);
    memset(m, 0, sizeof(m));
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", &u, &num);

        // 初始化
        flag[i] = 0;
        d[i] = -1;
        for (int j = 1; j <= num; j++) {
            scanf("%d", &v);
            m[u][v] = 1;
        }
    }
    bfs();
    for (int i = 1; i <= n; i++) {
        printf("%d %d\n", i, d[i]);
    }
    return 0;
}

12.5 连 通 分 量

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 100010;
int n, m;
int u, num, v;
int flag[maxx];
int color[maxx];
vector<int> G[maxx];

int c = 1;
void dfs(int t) {
    stack<int>s;
    s.push(t);
    color[t] = c;
    while (!s.empty()) {
        int u = s.top(); s.pop();
        int len = G[u].size();
        for (int i = 0; i < len; i++) {
            int v = G[u][i];
            if (flag[v] == 0) {
                color[v] = c;
                flag[v] = 1;
                s.push(v);
            }
        }
    }
}
int main()
{
    int q;
    memset(flag, 0, sizeof(flag));
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &u, &v);
        //将顶点v添加到G[u]数组中
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for (int i = 0; i < n; i++) {
        if (flag[i] == 0) {
            dfs(i);
            c++;
        }
    }
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d %d", &u, &v);
        if (color[u] == color[v]) {
            printf("yes\n");
        }
        else {
            printf("no\n");
        }
    }
    return 0;
}

第十三章 加权图

13.1 挑战问题之前——加权图

最小生成树

在这里插入图片描述
在这里插入图片描述

最短路径

在这里插入图片描述

13.2 最小生成树

在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 1010;
const int INF = 1 << 30;
int n, m[maxx][maxx];
int u, num, v;
int flag[maxx];

int prim() {
    // d数组是保存V-T, p数组是记录MST的
    int d[maxx], p[maxx];
    for (int i = 0; i < n; i++) {
        d[i] = INF;
        p[i] = -1;
        flag[i] = 0;
    }

    d[0] = 0;
    while (1) {
        int minv = INF;
        u = -1;
        // 比较出权值最小的边
        for (int i = 0; i < n; i++) {
            if (minv > d[i] && flag[i] == 0) {
                u = i;
                minv = d[i];
            }
        }

        if (u == -1)
            break;

        // 标记已经加入MST
        flag[u] = 1;

        // 更新未访问的点与已经访问的点之间的最小的边(相对于每个点)
        for (int v = 0; v < n; v++) {
            if (flag[v] == 0 && m[u][v] != INF && d[v] > m[u][v]) {
                d[v] = m[u][v];
                p[v] = u;
            }
        }
    }


    int ans = 0;
    for (int i = 0; i < n; i++)
        if (p[i] != -1)
            ans += m[i][p[i]];

    return ans;
}
int main()
{
    memset(flag, 0, sizeof(flag));
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &m[i][j]);
            if (m[i][j] == -1)
                m[i][j] = INF;
        }
    }
    printf("%d\n", prim());
    return 0;
}

13.3 单源最短路径

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 1010;
const int INF = 1 << 30;
int n, m[maxx][maxx];
int u, v ,k, c;
int flag[maxx];

void dijkstra() {
    int d[maxx];
    for (int i = 0; i < n; i++) {
        d[i] = INF;
        flag[i] = 0;
    }

    d[0] = 0;
    while (1) {
        int minv = INF;
        u = -1;
        // 比较出权值最小的边
        for (int i = 0; i < n; i++) {
            if (minv > d[i] && flag[i] == 0) {
                u = i;
                minv = d[i];
            }
        }

        if (u == -1)
            break;

        // 标记已经加入S
        flag[u] = 1;

        // 更新经由u结点,V-S的v结点的最小成本
        for (int v = 0; v < n; v++) {
            if (flag[v] == 0 && m[u][v] != INF && d[v] > d[u] + m[u][v]) {
                d[v] = d[u] + m[u][v];
            }
        }
    }

    for (int i = 0; i < n; i++)
        printf("%d %d\n", i, d[i] == INF ? -1 : d[i]);
}
int main()
{
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            m[i][j] = INF;
        }
    }
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &u, &k);
        for (int j = 0; j < k; j++) {
            scanf("%d %d", &v, &c);
            m[u][v] = c;
        }
    }
    dijkstra();
    return 0;
}

堆优化的单源最短路径

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int maxx = 10010;
const int INF = 1 << 30;
int n;
int flag[maxx];
// 加权有向图的邻接表表示法,pair.first是顶点v,pair.second是权值
vector<pair<int, int> > adj[maxx];

void dijkstra() {
    priority_queue<pair<int, int> > PQ;
    int d[maxx];
    for (int i = 0; i < n; i++) {
        d[i] = INF;
        flag[i] = 0;
    }

    d[0] = 0;
    PQ.push(make_pair(0, 0));
    while (!PQ.empty()) {

        pair<int, int> f = PQ.top(); PQ.pop();
        int u = f.second;

        // 标记已经加入S
        flag[u] = 1;

        //如果不是最小最短路径则忽略
        if (d[u] < f.first *(-1))
            continue;
        int len = adj[u].size();
        for (int j = 0; j < len; j++) {
            int v = adj[u][j].first;
            if (flag[v] == 0 && d[v] > d[u] + adj[u][j].second) {
                d[v] = d[u] + adj[u][j].second;
                // priority_queue默认取最大值,所以乘以-1
                PQ.push(make_pair(d[v] * (-1), v));
            }
        }
    }

    for (int i = 0; i < n; i++)
        printf("%d %d\n", i, d[i] == INF ? -1 : d[i]);
}
int main()
{
    int u, v, k, c;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &u, &k);
        for (int j = 0; j < k; j++) {
            scanf("%d %d", &v, &c);
            adj[u].push_back(make_pair(v, c));
        }
    }
    dijkstra();
    return 0;
}

第十四章 高等数据结构

14.1 互质的集合

在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

class DisjointSet {
    public :
        // rank记录树的高度
        vector<int> rank, p;
        DisjointSet() {};
        DisjointSet(int size) {
            rank.resize(size, 0);
            p.resize(size, 0);
            for (int i = 0; i < size; i++)
                makeSet(i);
        }
        void makeSet(int i) {
            p[i] = i;
            rank[i] = 0;
        }

        bool same(int x, int y) {
            return findSet(x) == findSet(y);
        }

        void unite(int x, int y) {
            link(findSet(x), findSet(y));
        }

        void link(int x, int y) {
            // 包含x的树的高度更高,则将y的树合并到x上
            if (rank[x] > rank[y])
                p[y] = x;
            else {
                p[x] = y;
                if (rank[x] == rank[y])
                    rank[y]++;
            }
        }
        int findSet(int x) {
            if (x != p[x])
                p[x] = findSet(p[x]);
            return p[x];
        }
};
int main()
{
    int n, a, b,t, q;
    scanf("%d %d", &n, &q);
    DisjointSet ds = DisjointSet(n);
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d", &t, &a, &b);
        if (t == 0)
            ds.unite(a, b);
        else
            printf("%d\n", ds.same(a, b) ? 1 : 0);
    }
    return 0;
}

第十五章 高等图算法

15.1 所有点对间最短路径

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxx = 110;
const LL INF = (1LL << 32);

int n,q;
LL d[maxx][maxx];

void floyd() {
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            if (d[i][k] == INF)
                continue;
            for (int j = 0; j < n; j++) {
                if (d[k][j] == INF)
                    continue;
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
}
int main()
{
    int u, v, c;
    scanf("%d %d", &n, &q);

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            d[i][j] = i == j ? 0 : INF;
    }

    // 输入权值
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d", &u, &v, &c);
        d[u][v] = c;
    }

    floyd();

    // 判断是否存在负环
    bool negative = false;
    for (int i = 0; i < n; i++)
        if (d[i][i] < 0)
            negative = true;

    if (negative)
        printf("NEGATIVE CYCLE\n");
    else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (d[i][j] == INF)
                    printf("INF");
                else
                    printf("%d", d[i][j]);
                printf("%c", j == n - 1 ? '\n' : ' ');
            }
        }
    }
    return 0;
}

15.2 拓 扑 排 序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <list>
#include <algorithm>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;

vector<int>G[maxx];
list<int>out;
bool flag[maxx];
int n;
int indeg[maxx];

void bfs(int s) {
    queue<int> q;
    q.push(s);

    while (!q.empty()) {
        int u = q.front(); q.pop();
        out.push_back(u);
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            indeg[v]--;
            if (indeg[v] == 0 && flag[v] == 0) {
                flag[v] = true;
                q.push(v);
            }
        }
    }
}

void tsort() {
    for (int i = 0; i < n; i++) {
        indeg[i] = 0;
        flag[i] = false;
    }

    for (int u = 0; u < n; u++) {
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            indeg[v]++;
        }
    }


    for (int u = 0; u < n; u++) {
        if (indeg[u] == 0 && flag[u] == 0)
            bfs(u);
    }

    for (list<int>::iterator it = out.begin(); it != out.end(); it++)
        printf("%d\n", *it);
}
int main()
{
    int s, t, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &s, &t);
        G[s].push_back(t);
    }

    tsort();
    return 0;
}

15.4 树 的 直 径

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;

class Edge {
public:
    int t, w;
    Edge() {};
    Edge(int t ,int w):t(t), w(w) {};
};
vector<Edge> G[maxx];
int n, d[maxx];
bool vis[maxx];
int cnt;

void bfs(int s) {
    for (int i = 0; i < n; i++)
        d[i] = INF;
    queue<int> Q;
    Q.push(s);
    d[s] = 0;

    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        int len = G[u].size();
        for (int i = 0; i < len; i++) {
            Edge e = G[u][i];
            if (d[e.t] == INF) {
                d[e.t] = d[u] + e.w;
                Q.push(e.t);
            }
        }

    }

}

void solve() {
    // 从任选的结点s出发,选择距离s最远的结点tgt
    bfs(0);

    int maxv = 0, tgt = 0;
    for (int i = 0; i < n; i++) {
        if (d[i] == INF)
            continue;
        if (maxv < d[i]) {
            maxv = d[i];
            tgt = i;
        }
    }


    //从tgt出发,求结点tgt到最远结点的距离maxv
    bfs(tgt);
    maxv = 0;
    for (int i = 0; i < n; i++) {
        if (d[i] == INF)
            continue;
        maxv = max(maxv, d[i]);
    }
    printf("%d\n", maxv);
}
int main() {

    int s, t, w;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++) {
        scanf("%d %d %d", &s, &t, &w);
        // 无向图
        G[s].push_back(Edge(t, w));
        G[t].push_back(Edge(s, w));
    }
    solve();
    return 0;
}

15.5 最小生成树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

第十七章 动态规划 略

第十八章 数论

算法之经典数学知识

第十九章 启发式搜索 略

19.1 八皇后问题

19.2 九宫格拼图

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值