【算法设计】贪心算法设计——均分纸牌、线段覆盖问题(C++实现)

本文介绍了贪心算法在解决均分纸牌和线段覆盖问题上的应用。通过计算纸牌堆的期望值并调整纸牌分布,找到最少移动次数的解决方案。线段覆盖问题则通过排序线段并逐步累加覆盖长度来求解。文章提供了C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡>𖥦<)!!
主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步!
更多算法分析与设计知识专栏:算法分析🔥
给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ

在这里插入图片描述


一、均分纸牌问题

问题描述

N堆纸牌,编号分别为1,2,…,n。每堆上有若干张,但纸牌总数必为n的倍数。可以在任一堆上取若干张纸牌,然后移动

移牌的规则为:

  • 在编号为1上取的纸牌,只能移到编号为2的堆上;
  • 在编号为n的堆上取的纸牌,只能移到编号为n-1的堆上;
  • 其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多

算法思想和解题思路

首先计算每堆牌的期望值,也就是每堆牌的总牌数与牌堆数的比值,也即为平均值,然后对每一堆牌的数量与平均值比较,如果当前堆的牌数少于平均值,就需要从下一堆牌中取出差值来补齐当前堆,有的时候会出现前面一堆中的牌数多于后面的一堆中的牌数,这个时候要用到的解法和前面的补给的办法恰好相反,只是将多余的补充出去到下一堆即可。

在这里插入图片描述

C++代码

#include <iostream>
using namespace std;
void minStep(int n,int *arr)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        sum += arr[i];
    }
    int avg = sum / n;
    for (int i = 0; i < n-1; i++)
    {
        if (arr[i] < avg)
        {
            cout<<i+2<<"---->"<<i+1<<":"<< avg - arr[i] << "张" << endl;
            arr[i + 1] = arr[i + 1] - (avg - arr[i]);
            arr[i] = arr[i]+ (avg - arr[i]);
        }

    }
    for (int i = 0; i < n - 1; i++)
    {
        if (arr[i] > avg)
        {
            cout << i+1  << "---->" << i + 2 << ":" << arr[i] - avg << "张" << endl;
            arr[i] = arr[i] - (arr[i] - avg);
            arr[i+1] = arr[i] + (arr[i] - avg);
        }
    }
}
int main()
{
    int n;
    cout << "输入一共几堆纸牌:";
    cin >> n;
    cout << "请分别输入" << n << "堆纸牌的个数:";
    int* a = new int[n];
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    minStep(n,a);
    delete[] a;
    return 0;
}

在这里插入图片描述

二、线段覆盖问题

问题描述

在一维空间中存在N条线段,每条线段的起始坐标与终止坐标已知,要求求出这些线段一共覆盖了多大的长度。

L1L2L3L4L5L6L7L8L9L10
起始坐标234567891011
终止坐标35769812101315

算法思想和解题思路

将所有的线段按照其起点坐标进行排序。如果起点的坐标相同,那么按照终点的坐标进行排序。

这样做的目的是为了保证线段在处理过程中始终按照从左到右的顺序排列

初始化一个变量Max,用来存储当前已经覆盖的线段的长度。然后,遍历排序后的线段列表

对于每一个线段,我们检查它的终点是否在当前已经覆盖的线段的最右边

  • 如果是,那么我们只需要将当前线段的长度加到Max上。

  • 如果不是,我们需要找到当前线段和已覆盖线段之间的所有线段,并将它们的长度累加到Max上。Max就是所有线段覆盖的总长度。

在这里插入图片描述

C++代码

#include <iostream>  
using namespace std;

void maxLine(int a[],int b[])
{
    // 初始化最大长度为两个数组的第一个元素之差,作为初始值  
    int Max = b[0] - a[0];

    // 循环遍历两个数组,i从1开始是因为我们只需要考虑非空线段  
    for (int i = 1, j = 0; i < 10; i++)
    {
        // 如果当前a[i] >= b[i],表示线段a[i]被线段b[i]覆盖  
        if (a[i] >= b[i])
        {
            // 更新最大长度为当前线段的长度加上之前被覆盖的最大长度  
            Max += (b[i] - a[i]);
            // 更新j为当前线段的索引,以便记录被覆盖的线段信息  
            j = i;
        }
        // 如果当前a[i] < b[i],表示线段a[i]无法被线段b[i]覆盖  
        else
        {
            // 如果b[i] <= b[j],表示当前线段b[i]也无法覆盖之前的线段b[j],因此跳过当前循环继续下一次循环  
            if (b[i] <= b[j]) continue;
            // 如果b[i] > b[j],表示当前线段b[i]可以覆盖之前的线段b[j],因此更新最大长度和j的值  
            else
            {
                Max += b[i] - b[j];
                j = i;
            }
        }
    }
    cout << "这些线段覆盖的最大长度为" << Max << endl;
}
int main() 
{
    int a[] = { 2,3,4,5,6,7,8,9,10,11 };   // 第一个数组,代表线段的起点  
    int b[] = { 3,5,7,6,9,8,12,10,13,15 }; // 第二个数组,代表线段的终点  
    maxLine(a, b);
    return 0;
}

在这里插入图片描述


在这里插入图片描述

大家的点赞、收藏、关注将是我更新的最大动力! 欢迎留言或私信建议或问题。
大家的支持和反馈对我来说意义重大,我会继续不断努力提供有价值的内容!如果本文哪里有错误的地方还请大家多多指出(●'◡'●)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天喜Studio

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

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

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

打赏作者

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

抵扣说明:

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

余额充值