C语言应用实例:硕鼠游戏,田忌赛马,搬桌子,活动选择(贪心算法)

一、硕鼠游戏

1.题目

小老鼠准备了 M 磅的猫粮,准备去和看守仓库的猫做交易,因为仓库里有小老鼠喜欢吃的五香豆。

仓库有 N 个房间;

第 i 个房间有 J[i] 磅的五香豆,并且需要用 F[i] 磅的猫粮去交换;

老鼠不必交换该房间所有的五香豆,换句话说,它可以用 F[i]∗a% 磅的猫粮去换取 J[i]∗a% 磅的五香豆,其中 a 是一个实数。

现在,请帮忙计算一下,小老鼠最多能够得到多少磅的五香豆?

++要求++:

输入包含多组测试用例。

每组测试数据首先一行是 2 个非负整数 M 和 N ,接着的 N 行,每行分别包含 2 个非负整数 J[i] 和 F[i] 。

输入数据以两个 −1 结束。

题目保证所有的数据不超过 1000

请计算并输出小老鼠最多能够得到的五香豆数量。

每组数据输出一行,保留 3 位小数。

2.题解

这是一个典型的贪心算法题

即不考虑全局,优先考虑局部最优解(全局是否最优需要证明)

代入题目即寻找能够获得最多猫粮的方案

有了思路,接下来便到了写代码的时候

读完要求我们会发现,每组数据有N个房间,每个房间至少包含两个数据(五香豆和猫粮),很明显,房间可以用一个结构体数组来表示,而数据则可以纳入对应结构体

结构体代码如下

cs 复制代码
struct mou{
  int beans;//存储房间内的豆子数量
  int mon;//存储需要缴纳的猫粮
    double mid;//计算单位数量的猫粮可兑换的豆子数量
};

核心算法如下

cs 复制代码
while(M){
        if(i<0){
            break;
        }
        if(M>=moulist[i].mon){
            sum+=moulist[i].beans;
            M-=moulist[i].mon;
        }else{
            sum+=moulist[i].mid*M;
            M=0;
        }
        i--;
    }

注:计算前先将结构体数组(moulist)按照mid大小排序

3.完整代码

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

struct mou{
  int beans;
  int mon;
    double mid;
};

int compare(const void *a,const void *b){
    struct mou *m1 = (struct mou*)a;
    struct mou *m2 = (struct mou*)b;
    if(m1->mid > m2->mid) return 1;
    if(m1->mid < m2->mid) return -1;
    return 0;
}

int main(){
    int M,N;
    while(1){
        scanf("%d %d",&M,&N);
        if(M==-1&&N==-1){
            break;
        }
        struct mou *moulist=(struct mou*)malloc(sizeof(struct mou)*N);
    for(int i=0;i<N;i++){
        struct mou m;
        scanf("%d %d",&m.beans,&m.mon);
        m.mid=(double)m.beans/m.mon;
        moulist[i]=m;
    }
        qsort(moulist,N,sizeof(struct mou),compare);
        double sum=0;
        int i=N-1;
    while(M){
        if(i<0){
            break;
        }
        if(M>=moulist[i].mon){
            sum+=moulist[i].beans;
            M-=moulist[i].mon;
        }else{
            sum+=moulist[i].mid*M;
            M=0;
        }
        i--;
    }
         printf("%.3lf\n", sum);
        free(moulist);
    }
    return 0;
}

二、田忌赛马

1.题目

"田忌赛马" 是中国历史上一个著名的故事。

大约2300年前,齐国大将田忌喜欢和国王赛马,并且约定:每赢一场,对方就要付 200 元。

假设已知田忌和国王的各自马匹的速度都不相同,请计算田忌最好的结果是什么。

++要求++:输入包含多组测试样例。

每组样例的第一行是一个整数 n(n<=1000),表示田忌和国王各自参赛的马匹数量。

接下来一行的 n 个整数表示田忌的马的速度,再接下来一行的 n个整数表示国王的马的速度。

n 为 0 时,表示输入数据的结束。

每组数据输出一行,表示田忌最多能够赢得的金额。

2.题解

这道题有两种情况:一种是田忌的马比国王的好,此时直接比就行;另一种是田忌的马比国王的差,此时则需要贪心算法

我们可以定义两个数组(t(田忌)和g(国王))用于存储马的质量

然后将二者分别排序便于后续操作

cs 复制代码
int compare(const void *a,const void *b){
    return (*(int*)a-*(int*)b);
}
int main(){
int t[n+1];
    int g[n+1];
    for(int i=0;i<n;i++){
        scanf("%d",&t[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&g[i]);
    }
    qsort(t,n,sizeof(int),compare);
    qsort(g,n,sizeof(int),compare);
}

核心算法如下

cs 复制代码
        int l=0;
        int r=n-1;
        int sum=0;
        for(int i=n-1;i>=0;i--){
        if(g[i]<t[r]){
            r--;
            sum++;
        }else if(g[i]>t[r]){
            l++;
            sum--;
        }
        }

3.完整代码

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

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

int main(){
    int n;
    while(1){
        scanf("%d",&n);
        if(n==0){
            break;
        }
    int t[n+1];
    int g[n+1];
    for(int i=0;i<n;i++){
        scanf("%d",&t[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&g[i]);
    }
    qsort(t,n,sizeof(int),compare);
    qsort(g,n,sizeof(int),compare);
        int l=0;
        int r=n-1;
        int sum=0;
        for(int i=n-1;i>=0;i--){
        if(g[i]<t[r]){
            r--;
            sum++;
        }else if(g[i]>t[r]){
            l++;
            sum--;
        }
        }
        printf("%d\n",sum*200);
    }
    return 0;
}

三、搬桌子

1.题目

已知楼层房间情况如下:

楼层中间是走廊,两侧各有 200 个房间,编号如图。

由于内部调整,需要把一些桌子从一个房间搬到另外的房间。

因为走廊很窄,但是桌子很大,所以同一段走廊每次只能通过一个桌子。

假设不论远近,每趟搬桌子都需要 10 分钟。

同时,当你从 房间i 搬桌子到 房间j 的过程中,房间i 到 房间j 之间的走廊都被占用,也就是说,在每个10分钟内,不能有多个任务共享同一段走廊。

现在,要完成所有的搬运任务,最少需要多少时间?

++要求:++ 输入包含 T 组测试用例。

每组测试用例首先是一个正整数 N(1<=N<=200),表示需要搬运的桌子数量。

接下来 N 行,每行包含 2 个正整数 s 和 t,表示需要将一个桌子从 房间s 搬到 房间t。

计算并输出完成所有的搬运任务需要的最少的时间,每组数据占一行。

2.题解

由题可知,中间一段走廊可以分为200段

那么如何找出总搬运时间呢?

其实很简单,我们只需用一个大小为201的数组来表示走廊,并初始化为0,然后每搬运一次就让其加一,最后找出最大重叠数乘10即可

(注意避开活动选择思想误区:即优先选择最早结束且满足要求的区间)

3.完整代码

cs 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main(){
    int T;
    scanf("%d",&T);
    for(int i=0;i<T;i++){
        int N;
        scanf("%d",&N);
        
        int corridor[201] = {0}; // 走廊段,索引1-200
        
        for(int j=0;j<N;j++){
            int s, t;
            scanf("%d %d",&s,&t);
            
            // 确保s <= t
            if(s > t){
                int temp = s;
                s = t;
                t = temp;
            }
            
            // 将房间号映射到走廊段
            // 房间1-2 → 走廊段1,房间3-4 → 走廊段2,以此类推
            int start = (s + 1) / 2;
            int end = (t + 1) / 2;
            
            // 标记被占用的走廊段
            for(int k=start; k<=end; k++){
                corridor[k]++;
            }
        }
        
        // 找出最大重叠数
        int max_overlap = 0;
        for(int j=1; j<=200; j++){
            if(corridor[j] > max_overlap){
                max_overlap = corridor[j];
            }
        }
        
        printf("%d\n", max_overlap * 10);
    }
    return 0;
}

4.错误写法(活动选择思想)

这里再贴一下主包一开始的错误写法(还超时了)

(这并不贪心,而是另一种并排的思想)

cs 复制代码
#include<stdio.h>
#include<math.h>
#include<stdlib.h>

struct Rom{
  int s,t;
    int num;
};

int compare(const void *a,const void *b){
    struct Rom *m1 = (struct Rom*)a;
    struct Rom *m2 = (struct Rom*)b;
    if(m1->t > m2->t) return 1;
    if(m1->t < m2->t) return -1;
    return 0;
}

int main(){
    int T;
    scanf("%d",&T);
    for(int i=0;i<T;i++){
        int N;
        scanf("%d",&N);
        struct Rom *romlist=(struct Rom*)malloc(sizeof(struct Rom)*N);
        for(int j=0;j<N;j++){
            struct Rom ro;
            scanf("%d %d",&ro.s,&ro.t);
            ro.num=1;
            romlist[j]=ro;
        }
        qsort(romlist,N,sizeof(struct Rom),compare);
        int a=N;
        int sum=0;
        int min=romlist[0].t;
        while(a){
        for(int j=0;j<N;j++){
            if(romlist[j].s>=min&&romlist[j].num!=0){
                sum++;
                romlist[j].num=0;
                a--;
                min=romlist[j].t;
            }
        }
        }
        printf("%d",sum*10);
    }
    return 0;
}

当笔记写了/doge

四、活动选择

1.题目

"今年暑假不AC?"

"是的。"

"那你干什么呢?"

"看世界杯呀,笨蛋!"

"@#$%^&*%..."

确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。

作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)

**++要求++:**输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Tis​,Tie​ (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。

对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。

2.题解

这是典型的活动选择题目

先定义节目结构体

cs 复制代码
struct Rom{
    int s,t;
};

然后定义节目结构体数组

cs 复制代码
struct Rom *romlist=(struct Rom*)malloc(sizeof(struct Rom)*N);

并将其按照结束时间排序

cs 复制代码
qsort(romlist,N,sizeof(struct Rom),compare);

贪心算法

cs 复制代码
int count = 1; // 至少可以选择第一个活动
        int last_end = romlist[0].t;
        
        for(int j=1;j<N;j++){
            if(romlist[j].s >= last_end){
                count++;
                last_end = romlist[j].t;
            }
        }

最后释放内存

3.完整代码

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

struct Rom{
    int s,t;
};

int compare(const void *a,const void *b){
    struct Rom *m1 = (struct Rom*)a;
    struct Rom *m2 = (struct Rom*)b;
    if(m1->t > m2->t) return 1;
    if(m1->t < m2->t) return -1;
    return 0;
}

int main(){
    int N;
    while(1){
        scanf("%d",&N);
        if(N==0){
            break;
        }
        struct Rom *romlist=(struct Rom*)malloc(sizeof(struct Rom)*N);
        for(int j=0;j<N;j++){
            scanf("%d %d",&romlist[j].s,&romlist[j].t);
        }
        
        qsort(romlist,N,sizeof(struct Rom),compare);
        
        int count = 1; 
        int last_end = romlist[0].t;
        
        for(int j=1;j<N;j++){
            if(romlist[j].s >= last_end){
                count++;
                last_end = romlist[j].t;
            }
        }
        
        printf("%d\n",count);
        free(romlist);
    }
    return 0;
}
相关推荐
爱吃甜品的糯米团子2 小时前
详解 JavaScript 内置对象与包装类型:方法、案例与实战
java·开发语言·javascript
知花实央l2 小时前
【Web应用实战】 文件上传漏洞实战:Low/Medium/High三级绕过(一句话木马拿webshell全流程)
前端·学习·网络安全·安全架构
snakecy2 小时前
信息系统项目管理师--论文case
大数据·学习·职场和发展·区块链
QT 小鲜肉2 小时前
【Git、GitHub、Gitee】按功能分类汇总Git常用命令详解(超详细)
c语言·网络·c++·git·qt·gitee·github
天狗下凡2 小时前
【OpenGL学习】第2课:用OpenGL创建第一个窗口
学习
郝学胜-神的一滴2 小时前
Linux下,获取子进程退出值和异常终止信号
linux·服务器·开发语言·c++·程序人生
AI科技星2 小时前
张祥前统一场论动量公式P=m(C-V)误解解答
开发语言·数据结构·人工智能·经验分享·python·线性代数·算法
嘉年华-cocos2 小时前
考研英语 5400 纯数据版
学习·英语·背单词·音标
海琴烟Sunshine2 小时前
leetcode 345. 反转字符串中的元音字母 python
python·算法·leetcode