C语言——基础查漏补缺(四):利用《挑战程序设计竞赛(第二版)》前两章学习经典算法

相关文章:
C语言——基础查漏补缺(一):超长文帮你理清一些概念
C语言——基础查漏补缺(二):《C程序设计试题汇编》应试概念总结
C语言——基础查漏补缺(三):谭浩强红书刷题笔记大杂烩

这是《挑战程序设计竞赛(第二版)》前两章的学习笔记,包含了贪心法、动态规划、DFS和BFS等,自认为是经典的基础算法补充学习资料。

第一章的热身

1,三角形

// 三角形,输入n,输入n个数(备选边长)
// 输出最大三角形边长,如果无法组成三角形就输出0
// 算法:暴搜

#include<stdio.h>
#define maxn 105

int max(int a, int b){
    if(a >= b)return a;
    else return b;
}

// 判断三条边是否能组成三角形
int judge(int a, int b, int c){
    int m = max(a, max(b, c));
    int len = a + b + c;
    int remain = len - m;
    if(remain > m)return 1;
    else return 0;
}

int main(){
    int a[maxn], n;
    int len = 0, temp;
    scanf("%d", &n);
    for(int i = 0;i < n;i++){
        scanf("%d", a + i);
    }
    for(int i = 0;i < n;i++)
        for(int j = i + 1;j < n;j++)
            for(int k = j + 1;k < n;k++){
                if(judge(a[i], a[j], a[k])){
                    temp = a[i] + a[j] + a[k];
                    len = (temp > len) ? temp : len;
                }
            }
    printf("%d\n", len);
    return 0;
}

2,蚂蚁爬杆

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

// POJ 1852 蚂蚁爬杆
// 精髓在于认识到蚂蚁相遇折回相当于相遇穿过

#include<stdio.h>
#define maxn 105

int min(int a, int b){
    if(a <= b)return a;
    else return b;
}

int max(int a, int b){
    if(a >= b)return a;
    else return b;
}

int main(){
    int L, n, a[maxn];
    int minres = 0, maxres = 0;
    scanf("%d %d", &L, &n);
    for(int i = 0;i < n;i++)
        scanf("%d", a + i);
    // 选择最短时间
    for(int i = 0;i < n;i++){
        minres = max(minres, min(a[i], L - a[i]));
    }
    // 选择最长时间
    for(int i = 0;i < n;i++){
        maxres = max(maxres, max(a[i], L - a[i]));
    }
    printf("%d\n", minres);
    printf("%d\n", maxres);
    return 0;
}

3,二分搜索解决抽签问题

在这里插入图片描述
// 抽签问题 输入n和m,输入n个数,如果存在4个数
// 的和是m,则输出yes,否则输出no
// 本来是要用四个for嵌套,改进方法就是将最内层的for变成二分查找即可

#include<stdio.h>
#define maxn 105

// 二分查找,找到返回1,找不到返回0
int search(int a[], int start, int end, int x){
    if(start > end)return 0;
    int mid = (start + end) / 2;
    if(a[mid] == x)return 1;
    else if(a[mid] > x)
        return search(a, start, mid - 1, x);
    else
        return search(a, mid + 1, end, x);
}

int main(){
    int n, m, a[maxn];
    scanf("%d %d", &n, &m);
    for(int i = 0;i < n;i++)
        scanf("%d", a + i);
    for(int i = 0;i < n;i++)
        for(int j = 0;j < n;j++)
            for(int k = 0;j < n;j++){
            	// 本来这是最内层的for,现改成二分查找即可
                if(search(a, 0, n - 1, m - a[i] - a[j] - a[k])){
                    printf("yes\n");
                    return 0;
                }
            }
    printf("no\n");
    return 0;
}

第二章经典基础算法学习

DFS和BFS

☆1,用DFS解决部分和问题

在这里插入图片描述
// 部分和问题v1.0 要求用dfs。
// 输入n,输入n个数,输入k
// 如果有若干个数之和为k,输出yes,反之输出no

#include<stdio.h>

int a[105], n, k;

// 前i项a[0]-a[i-1]已经得到sum的同时,能否满足最终得到k
int dfs(int i, int sum){
    if(i == n)
        return sum == k;
    // 加上a[i]
    if(dfs(i + 1, sum + a[i]))
        return 1;
    // 不加上a[i]
    if(dfs(i + 1, sum))
        return 1;
    return 0;
}

int main(){
    scanf("%d", &n);
    for(int i = 0;i < n;i++)
        scanf("%d", a + i);
    scanf("%d", &k);
    if(dfs(0, 0) == 1)
        printf("yes\n");
    else
        printf("no\n");
    return 0;
}

2,DFS数水洼

在这里插入图片描述
(输入N,M,输入NM的矩阵图形;输出水洼的数量)
// DFS数水洼问题。输入n和m,输入n
m大小矩阵,
// w代表有水,.代表没水;输出水洼数量

#include<stdio.h>

int n, m;
char field[105][105];

// 从x行y列开始进行dfs
void dfs(int x, int y){
    // 先将当前点的水消去
    field[x][y] = '.';

    for(int dx = -1;dx <= 1;dx++)
        for(int dy = -1;dy <= 1;dy++){
            int nx = x + dx, ny = y + dy;
            // if(field[nx][ny] == 'w'
            // && nx < n && nx >= 0
            // && ny < m && ny >= 0){
            //     dfs(nx, ny);
            // }
            if(nx < n && nx >= 0
            && ny < m && ny >= 0
            && field[nx][ny] == 'w'){
                dfs(nx, ny);
            }
        }
}

int main(){
    int count = 0;
    scanf("%d %d", &n, &m);
    getchar();
    for(int i = 0;i < n;i++){
        for(int j = 0;j < m;j++){
            field[i][j] = getchar();
        }
        getchar();
    }
    for(int i = 0;i < n;i++)
        for(int j = 0;j < m;j++){
            if(field[i][j] == 'w'){
                dfs(i, j);
                count++;
            }
        }
    printf("%d\n", count);
    return 0;
}

注意上述代码中注释掉的if语句,如果那样做可能会造成数组越界,因为if中有多个判断的时候,会从左至右进行判断。

3,☆☆BFS迷宫最短路径

在这里插入图片描述
输出从起点到终点的最短路径(上图是22)

数据结构:和地图相同大小的二维整型数组,存储开始结点到当前结点的距离,如果结点没有被访问过,则存储INF。

算法描述:

  • 将起点入栈;
  • 如果栈不空,则出栈,如果当前结点是终点,则停止算法,输出起点到当前结点的距离;如果当前结点不是终点,则依次遍历它的四个方向的邻点,如果邻点没有被访问过,且它不是墙壁,则入栈,返回此步起点。

贪心法

1,硬币问题

// 硬币问题, 分别输入1、5、10、50、100、500元硬币数量
// 输入A,输出支付A一共需要的最少硬币数量,假设一定有一种办法能刚好付清
// 算法:尽可能多用大面值即可

#include<stdio.h>

int min(int a, int b){
    return (a < b) ? a : b;
}

int main(){
    int c[6], A, count = 0;
    int v[6] = {1, 5, 10, 50, 100, 500};
    for(int i = 0;i < 6;i++)
        scanf("%d", c + i);
    scanf("%d", &A);
    for(int i = 5;i >= 0;i--){
        int t = min(A / v[i], c[i]);
        A = A - t * v[i];
        count += t;
    }    
    printf("%d\n", count);
    return 0;
}

2,区间调度问题

// 区间调度问题,输入n,输入n个数是n个工作开始时间
// 再输入n个数,是n个工作结束时间,输出最多参与几个工作
// 算法:可选的工作中,每次选择结束时间最早的工作

#include<stdio.h>
#define maxn 105

int s[maxn], t[maxn], n;

// 按照各个工作的结束时间进行排序
void sort(){
    int temp;
    for(int i = 0;i < n - 1;i++)
        for(int j = 0;j < n - 1 - i;j++){
            if(t[j] < t[j + 1]){
                temp = t[j];
                t[j] = t[j + 1];
                t[j + 1] = temp;
                temp = s[j];
                s[j] = s[j + 1];
                s[j + 1] = temp;
            }
        }
}

int main(){
    int flag[maxn], count = 0;
    scanf("%d", &n);
    for(int i = 0;i < n;i++)
        scanf("%d", s + i);
    for(int i = 0;i < n;i++)
        scanf("%d", t + i);
    sort();
    for(int i = 0;i < n;i++)
        flag[i] = 0;    // 值为1代表已经被选择(或者不能选择)
    for(int i = n - 1;i >= 0;i--){
        if(flag[i] == 1)
            continue;
        // 如果可以选择,才会运行到这里
        count++;
        flag[i] = 1;
        // 将不能和i对应工作同时选择的工作标注为不可选
        for(int j = i - 1;j >= 0;j--){
            if(s[j] < t[i])
                flag[j] = 1;
        }
    }
    printf("%d\n", count);
    return 0;
}

3,字典序最小问题

// 字典序最小问题。输入n,输入长度为n的字符串,
// 每次要么从首部要么从尾部取一个字母,放到新字符串(初始为空)
// 的末尾,需要使新字符串最小。输出新字符串

比如说输入

6
ACDBCB
ABCBCD

算法:只需要比较首尾谁小即可,如果遇到一样的情况,比如上述输入,剩下CDBC时,只需要将此子字符串和它自己的反序比较一下即可,它的字典序更小,就选首部,如果它的反序字典序更小,就选尾部。

#include<stdio.h>
#include<stdlib.h>
#define maxn 105

int n;
char c[maxn];

// 从start到end的子字符串,正序比反序字典序小则返回1
int compare(char c[], int start, int end){
    for(int i = start, j = end;i <= end;i++, j--){
        if(c[i] < c[j])return 1;
        if(c[i] > c[j])return 0;
    }
    return 0;
}

int main(){
    int s, t;
    scanf("%d", &n);
    getchar();  // 吸收回车
    char * res = (char *)malloc(sizeof(char));
    for(int i = 0;i < n;i++)
        scanf("%c", c + i);
    s = 0;
    t = n - 1;
    for(int i = 0;i < n;i++){
        if(c[s] < c[t]){
            res[i] = c[s];
            s++;
        }
        else if(c[s] > c[t]){
            res[i] = c[t];
            t--;
        }
        else{
            if(compare(c, s, t) == 1){
                res[i] = c[s];
                s++;
            }
            else{
                res[i] = c[t];
                t--;
            }
        }
    }
    for(int i = 0;i < n;i++)
        printf("%c", res[i]);
    printf("\n");
    free(res);
    return 0;
}

4,标记点问题

在这里插入图片描述

// 标记点问题。
// 输入n,输入n个点的坐标,输入R,对若干点标记。要求每
// 个点要么自己被标记,要么距离标记点R以内,输出标记点个数

算法如图,一目了然:
在这里插入图片描述

#include<stdio.h>
#define maxn 105

int n, r, c[maxn];

int main(){
    int temp, count = 0, i;
    scanf("%d %d", &n, &r);
    for(int i = 0;i < n;i++)
        scanf("%d", c + i);
    i = 0;
    while(i < n){
        // 存储没标记也被标记点覆盖的最左结点坐标
        temp = c[i];
        while(i < n && c[i] - temp <= r)i++;
        // 给点i-1添加标记
        temp = c[i - 1];
        while(i < n && c[i] - temp <= r)i++;	// 跳出循环时,i是不能被刚标记的点覆盖的最左结点
        count++;
    }
    printf("%d\n", count);
    return 0;
}

5,哈夫曼的应用(总是选最小的两个)

在这里插入图片描述

#include<stdio.h>
#define maxn 105

int n, a[maxn];

void sort(int a[], int s, int t){
    int flag = 0, i, j, temp;
    for(i = 0;i < t - s;i++){
        for(j = s;j <= t - 1 - i;j++){
            if(a[j] > a[j - 1]){
                temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                flag = 1;
            }
        }
        if(flag == 0)break;
    }
}

int main(){
    int cost = 0;
    scanf("%d", &n);
    for(int i = 0;i < n;i++)
        scanf("%d", a + i);
    for(int i = 0;i < n - 1;i++){
        sort(a, i, n - 1);
        cost += (a[i] + a[i + 1]);
        a[i + 1] += a[i];
    }
    printf("%d\n", cost);
    return 0;
}

动态规划

看我的另一篇文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值