算法回忆录(2)

6.输入一个非递减排列的整数数组nums,和一个目标值target。请找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值target,则输出0,0。请设计一个时间复杂度为0(log n)的算法解决此问题。

输入包括两行,第一行输入两个整数,分别表示数组的长度和target,第二行输入数组nums。输出为一个整数。

例如输入:

6 8

5 7 7 8 8 10

则输出: 4 5

若输入:

6 9

5 7 7 8 8 10

则输出: 0 0

#include <stdio.h>  
  
// 二分查找的变种,用于找到目标值的左边界  
int findLeftBound(int* nums, int numsSize, int target) {  
    int left = 0, right = numsSize - 1;  
    int leftBound = -1; // 初始化左边界为-1,表示未找到  
    while (left <= right) {  
        int mid = left + (right - left) / 2;  
        if (nums[mid] >= target) {  
            right = mid - 1; // 尝试在左半部分查找  
            leftBound = mid; // 更新左边界  
        } else {  
            left = mid + 1;  
        }  
    }  
    return leftBound;  
}  
  
// 二分查找的变种,用于找到目标值的右边界  
int findRightBound(int* nums, int numsSize, int target) {  
    int left = 0, right = numsSize - 1;  
    int rightBound = -1; // 初始化右边界为-1,表示未找到  
    while (left <= right) {  
        int mid = left + (right - left) / 2;  
        if (nums[mid] <= target) {  
            left = mid + 1; // 尝试在右半部分查找  
            rightBound = mid; // 更新右边界  
        } else {  
            right = mid - 1;  
        }  
    }  
    return rightBound;  
}  
  
int main() {  
    int numsSize, target;  
    scanf("%d %d", &numsSize, &target);  
    int nums[numsSize];  
    for (int i = 0; i < numsSize; i++) {  
        scanf("%d", &nums[i]);  
    }  
  
    int leftBound = findLeftBound(nums, numsSize, target);  
    int rightBound = findRightBound(nums, numsSize, target);  
  
    // 如果左边界仍为-1,说明未找到目标值  
    if (leftBound == -1) {  
        printf("0 0\n");  
    } else {  
        printf("%d %d\n", leftBound + 1, rightBound + 1); // 数组索引从0开始,输出从1开始  
    }  
  
    return 0;  
}

解释和步骤:

  1. 函数 findStartIndex

    • 使用修改版的二分查找来找到目标值在数组中的起始位置。
    • 初始化左右边界,然后在循环中根据中间元素与目标值的比较调整左右边界。
    • 如果找到目标值,更新 start 并继续向左查找。
  2. 函数 findEndIndex

    • 使用同样的修改版二分查找来找到目标值在数组中的结束位置。
    • 初始化左右边界,然后在循环中根据中间元素与目标值的比较调整左右边界。
    • 如果找到目标值,更新 end 并继续向右查找。
  3. 主函数 main

    • 输入数组的长度和目标值。
    • 输入排序后的整数数组。
    • 调用 findStartIndexfindEndIndex 函数找到目标值的起始位置和结束位置。
    • 如果找到了起始位置和结束位置,则输出它们;否则输出 "0 0" 表示目标值不存在于数组中。

运行结果:

7. 残缺棋盘是一个有2k×2k (k≥1)个方格的棋盘,其中恰有一个方格残缺。 下图给出k=1时各种可能的残缺棋盘,其中残缺的方格用阴影表示。

图中的棋盘称作“三格板”,残缺棋盘问题就是用这四种三格板覆盖更大的残缺棋盘。在覆盖中要求: 1)两个三格板不能重叠 2)三格板不能覆盖残缺方格,但必须覆盖其他所有方格 编程输入k及残缺格的坐标,求残缺棋盘覆盖方法。 输入:三个整数,分别表示k值和残缺格的坐标; 输出:2k×2k矩阵,具体数值为覆盖的序号。

#include<stdio.h>
#include<math.h>
void TileBoard(int tr,int tc,int dr,int dc,int size);
void OutputBoard(int size);
int tile=1;
int Board[1025][1025];
int main()
{
 int n,a,b;
 scanf("%d",&n);
 int sum;
 sum=pow(2,n);
 scanf("%d %d",&a,&b);
 Board[n][n]=0;
 TileBoard(0,0,a,b,sum);
 OutputBoard(sum);
 return 0;
}
void TileBoard(int tr,int tc,int dr,int dc,int size)
{
 if(size==1) return;
 int t=tile++,
 s=size/2;
 if(dr<tr+s&&dc<tc+s)
 TileBoard(tr,tc,dr,dc,s);
 else
 {
 Board[tr+s-1][tc+s-1]=t;
 TileBoard(tr,tc,tr+s-1,tc+s-1,s);
 }
 if(dr<tr+s&&dc>=tc+s)
TileBoard(tr,tc+s,dr,dc,s);
 else 
 {
 Board[tr+s-1][tc+s]=t;
 TileBoard(tr,tc+s,tr+s-1,tc+s,s);
 }
 if(dr>=tr+s&&dc<tc+s)
 TileBoard(tr+s,tc,dr,dc,s);
 else
 { 
 Board[tr+s][tc+s-1]=t;
 TileBoard(tr+s,tc,tr+s,tc+s-1,s);
 }
 if(dr>=tr+s&&dc>=tc+s)
TileBoard(tr+s,tc+s,dr,dc,s);
 else 
 {
 Board[tr+s][tc+s]=t;
 TileBoard(tr+s,tc+s,tr+s,tc+s,s);
 }
}
 void OutputBoard(int size)
 {
 for(int i=0;i<size;i++)
 {
 for(int j=0;j<size;j++){
 printf("%-3d",Board[i][j]);
 }
 printf("\n");
 }
 printf("end");
 }

 运行结果:

8. 活动安排问题求解: 假设某社团某一天要组织n个活动E={1,2,3...n},其中每个活动都要求使用同一礼堂,而且在同一时间内只有一个活动能使用这个礼堂。每个活动i都有一个要求使用礼堂的起始时间si和结束时间fi, 且有si<fi,。若区间(si,fi,)和(sj,fj,)不相交,则称活动i与活动j是相容的。

现在给定n个活动的开始时间和结束时间,请设计一个活动安排方案,使得安排的相容活动数目最多。

表1 活动时间表

1

2

3

4

5

6

7

8

10

11

12

1

2

0

5

3

5

6

8

2

12

15

3

4

5

7

8

9

10

11

13

14

18

#include <stdio.h>  
#include <stdlib.h>  
  
// 定义活动结构体,包含活动的索引、开始时间和结束时间  
struct Activity {  
    int index;     // 活动的索引或编号  
    int start;     // 活动的开始时间  
    int end;       // 活动的结束时间  
};  
  
// 比较函数,用于qsort,根据活动的结束时间进行排序  
// 返回值小于0表示a小于b,大于0表示a大于b,等于0表示a等于b  
int compare(const void *a, const void *b) {  
    struct Activity *activityA = (struct Activity *)a; // 将void指针转换为Activity指针  
    struct Activity *activityB = (struct Activity *)b; // 将void指针转换为Activity指针  
    return (activityA->end - activityB->end); // 返回两个活动结束时间的差值  
}  
  
int main() {  
    int n = 12; // 活动的总数  
      
    // 初始化活动数组  
    struct Activity activities[] = {  
        {1, 1, 3},  
        {2, 12, 14},  
        {3, 0, 5},  
        {4, 5, 7},  
        {5, 6, 10},  
        {6, 3, 8},  
        {7, 8, 12},  
        {8, 5, 9},  
        {9, 8, 11},  
        {10, 2, 13},  
        {11, 2, 4},  
        {12, 15, 18}  
    };  
      
    // 使用qsort函数对活动数组进行排序,根据活动的结束时间  
    qsort(activities, n, sizeof(struct Activity), compare);  
      
    // 初始化计数器,用于记录被安排的活动数量  
    int counter = 1; // 至少可以安排一个活动  
      
    // 初始化最后一个已安排活动的结束时间  
    int last_end_time = activities[0].end; // 第一个活动的结束时间  
      
    // 打印第一个被安排的活动  
    printf("第%d个活动被安排: %d开始, %d结束.\n", activities[0].index, activities[0].start, activities[0].end);  
      
    // 遍历剩余的活动,尝试安排它们  
    for (int i = 1; i < n; i++) {  
        // 如果当前活动的开始时间不小于上一个已安排活动的结束时间,则可以安排  
        if (activities[i].start >= last_end_time) {  
            counter++; // 增加计数器  
            printf("第%d个活动被安排: %d开始, %d结束.\n", activities[i].index, activities[i].start, activities[i].end);  
              
            // 更新最后一个已安排活动的结束时间  
            last_end_time = activities[i].end;  
        }  
    }  
      
    // 打印总计被安排的活动数量  
    printf("总计%d个活动被安排\n", counter);  
      
    return 0;  
}

运行结果:

9. 设有n个独立的作业{1,2…,n},由m台相同的机器进行加工处理。作业i所需时间为Ti。约定任何作业可以在任何一台机器上加工处理,但未完工前不允许中断处理,任何作业不能拆分成更小的子作业。要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。输入第一行输入n和m;第二行输入n个整数,分别表示n个作业做需要的时间,输出结果为一个整数,表示完成任务的最短时间。

#include <stdio.h>
#include <stdlib.h>
 
// 比较函数,用于排序作业
int compare(const void *a, const void *b) {
    return (*(int*)b - *(int*)a);
}
 
// 计算完成任务的最短时间
int shortestCompletionTime(int n, int m, int jobs[]) {
    // 按处理时间从大到小排序作业
    qsort(jobs, n, sizeof(int), compare);
 
    // 初始化每台机器的当前总处理时间为0
    int machines[m];
    for (int i = 0; i < m; i++) {
        machines[i] = 0;
    }
 
    // 将作业分配给机器
    for (int i = 0; i < n; i++) {
        // 找到当前处理时间最短的机器
        int minIndex = 0;
        for (int j = 1; j < m; j++) {
            if (machines[j] < machines[minIndex]) {
                minIndex = j;
            }
        }
        // 分配作业给当前处理时间最短的机器,并更新其总处理时间
        machines[minIndex] += jobs[i];
    }
 
    // 找到所有机器中总处理时间最长的时间
    int maxTime = machines[0];
    for (int i = 1; i < m; i++) {
        if (machines[i] > maxTime) {
            maxTime = machines[i];
        }
    }
 
    return maxTime;
}
 
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
 
    int jobs[n];
    for (int i = 0; i < n; i++) {
        scanf("%d", &jobs[i]);
    }
 
    // 计算并输出完成任务的最短时间
    int shortestTime = shortestCompletionTime(n, m, jobs);
    printf("%d", shortestTime);
    return 0;
}

运行结果:

10. n个孩子站成一排。输入一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发苹果:每个孩子至少分配到1个苹果。相邻两个孩子评分更高的孩子会获得更多的苹果。请你给每个孩子分发苹果,计算并返回需要准备的最少苹果数目。

#include <stdio.h>
 
int minApples(int ratings[], int n) {
    if (n <= 0) return 0;
    int apples[n];
    for (int i = 0; i < n; ++i) apples[i] = 1; // 每个孩子至少分配一个苹果
    
    // 从左向右扫描,保证右边评分高的孩子拿到的苹果比左边多
    for (int i = 1; i < n; ++i) {
        if (ratings[i] > ratings[i - 1]) {
            apples[i] = apples[i - 1] + 1;
        }
    }
    
    // 从右向左扫描,保证左边评分高的孩子拿到的苹果比右边多
    for (int i = n - 2; i >= 0; --i) {
        if (ratings[i] > ratings[i + 1] && apples[i] <= apples[i + 1]) {
            apples[i] = apples[i + 1] + 1;
        }
    }
    
    int totalApples = 0;
    for (int i = 0; i < n; ++i) {
        totalApples += apples[i];
    }
    
    return totalApples;
}
 
int main() {
    int n;
    scanf("%d", &n);
    int ratings[n];
    for (int i = 0; i < n; ++i) {
        scanf("%d", &ratings[i]);
    }
    
    int minTotalApples = minApples(ratings, n);
    printf("%d", minTotalApples);
    
    return 0;
}

运行结果:

 结语

宝剑锋从磨砺出

梅花香自苦寒来

!!!

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT 青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值