贪心算法以及常见的leetcode 算法求解--c++

说明:本文引用leetcode :455,452,122,605题。

介绍:

贪心算法(Greedy Algorithm)是一种解决问题的算法策略,它在每一步都会选择当前情况下看起来最优的选择,而不考虑未来可能发生的情况。贪心算法通常在问题具备“贪心选择性质”时使用,即选择当前最优解似乎能够导致全局最优解。虽然贪心算法不一定对所有问题都有效,但它在一些特定类型的问题上表现出色,因为它具有高效性和相对简单的实现。以下是贪心算法的一般步骤:

  1. 问题建模: 将问题抽象成一个数学模型,明确定义问题的目标和约束条件。

  2. 制定选择策略: 在每一步中,根据某个标准选择当前看起来最优的解决方案。这个选择策略是贪心算法的核心,通常涉及权衡局部最优和全局最优的考虑。

  3. 检验可行性: 确保所选的解决方案满足问题的约束条件,如果不满足,需要舍弃该解决方案。

  4. 更新问题状态: 根据所选的解决方案,更新问题的状态,准备进行下一步的选择。

  5. 重复步骤2到步骤4: 迭代执行,直到达到问题的终止条件。贪心算法在许多领域都有应用,例如:

  • 最小生成树问题:在图论中,Prim算法和Kruskal算法是两个经典的贪心算法,用于找到一个图的最小生成树。

  • 背包问题:在组合优化中,有多种变体的背包问题,可以使用贪心算法来找到接近最优解的解决方案。

  • 调度问题:在任务调度和作业调度中,贪心算法可以用来选择最优的任务或作业顺序。

  • 霍夫曼编码:在信息编码中,霍夫曼编码是一种基于字符频率的贪心算法,用于实现数据的无损压缩。

需要注意的是,贪心算法并不总是能够找到全局最优解,因为它只关注当前的最优选择,而不考虑长远影响。因此,在应用贪心算法时,需要仔细分析问题的性质,以确保贪心策略适用于特定问题。

1.经典问题。

#include 
#include 
#include 
using namespace std;

/*

假设1元、2元、5元、10元、20元、50元、
100元的纸币分别有a,b,c,d,e,f,g张。
现在要用这些钱来支付m元,至少要用多少张纸币?
用贪心算法的思想,每一次选择最大面值的钱币

*/

int main()
{
    int sum = 0;
    cout << "please input your money" << endl;
    int A;
    int temp[6];
    int moneyval[6] = {1, 5, 10, 20, 50, 100}; //每种硬币的面值
    cin >> A;

    for (int i = 5; i >= 0; i--)
    {

        temp[i] = (A / moneyval[i]);

        A -= (temp[i] * moneyval[i]);

        sum += temp[i];
    }

    cout << sum << endl;

    system("pause");
    return 0;
}

// int main()
// {

//     while (j = 3)
//     {

//         int i;
//         int temp;
//         cout << "input your money:" << endl;
//         cin >> A;
//         cout << "nums of every coin:" << endl;
//         for (int i = 5; i >= 0; i--)

//         { //贪心策略,有限选择面值大的票子

//             cout << "----" << A << "与" << moneyval[i] << "进行比较"
//                  << "----" << endl;
//             temp = A / moneyval[i]; //使用temp 记录使用票子i 的枚数 比如45,就需要找两张20 temp 就是需要找的钱的个数。
//             A -= (temp * moneyval[i]);
//             if (temp != 0) //判断是否找零成功
//             {
//                 sum += temp;
//             }
//             cout << "least give" << temp << "piece" << moneyval[i] << ""
//                  << "left:" << A << endl;
//         }

//         cout << "total num:" << sum << endl; //最少票子总数
//         sum = 0;
//         cout << "exit(yes1?no:0):" << endl;
//         cin >> j;
//         if (j == 1)
//             break;

//         if (j == 0)
//             continue;
//         else
//         {
//             cout << "illeagual operation:" << endl;
//             break;
//         }
//     }

//     system("pause");
//     return 0;
// }

//贪心算法汇总
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

void PrintVector2D(const vector> &array)
{

    for (const auto &row : array)
    {
        cout << "[";
        for_each(row.begin(), row.end(), [](int num)
                 { cout << num << " "; });
        cout << "]";
        cout << endl;
    }
}

struct ListNode
{
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

struct TreeNode
{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(nullptr), right(nullptr) {}
};

// https://leetcode.cn/problems/assign-cookies/ leetcode 455 分发饼干问题。
/*

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,
都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,
这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

=============================================================================
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

==============================================================================
示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.


*/

class Solution1
{
public:
    int findContentChildren(vector &g, vector &s)
    {

        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int gr = g.size() - 1;
        int sr = s.size() - 1;

        int n = 0;
        while (gr >= 0 && sr >= 0)
        {

            if (s[sr] >= g[gr])
            {

                sr--;
                gr--;
                n++;
            }

            else
            {
                gr--;
            }
        }
        // return n;

        return n;
    }
};

void mytest01()
{

    vector g = {1, 2, 3};
    vector s = {1, 1};

    Solution1 s1;
    auto res = s1.findContentChildren(g, s);
    cout << res << endl;
}
/*
https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/ 452
452. 用最少数量的箭引爆气球
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,
其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。
在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。

示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。
示例 2:

输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。
示例 3:

输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
- 在x = 2处发射箭,击破气球[1,2]和[2,3]。
- 在x = 4处射出箭,击破气球[3,4]和[4,5]。




*/

// class Solution2
// {
// public:
//     int findMinArrowShots(vector> &points)
//     {

//         if (points.empty())
//         {

//             return 0;
//         }

//     //排序的时间复杂度是nlogn
//         sort(points.begin(), points.end(), [](vector &u1, vector &u2)
//              { return u1[1] < u2[1]; });

//         int pos = points[0][1];
//         int ans = 1;

//         for (const vector &ballon : points)
//         {
//             if (ballon[0] > pos)
//             {
//                 pos = ballon[1];
//                 ans++;
//             }
//         }

//         return ans;
//     }
// };

class Solution2
{
public:
    int findMinArrowShots(vector> &points)
    {
        //有边界
        //     if (points.empty())
        //     {
        //         return 0;
        //     }
        //     int n = 1;

        //     sort(points.begin(), points.end(), [](vector &v1, vector &v2)
        //          { return v1[1] < v2[1]; });
        //          //1                                                             //2
        //     // {{0, 6}, {3, 8}, {6, 8}, {2, 8}, {3, 9}, {2, 9}, {0, 9}, {3, 9},/ {9, 10}, {7, 12}};
        //     int pos = points[0][1];

        //     for (const auto p : points)
        //     {

        //         if (p[0] > pos)
        //         {
        //             pos = p[1];
        //             n++;
        //         }
        //     }

        //     return n;
        // }
        // 、、、、、左边界的做法。

        sort(points.begin(), points.end());

        int right = points[0][1], n = 1;

        for (const auto p : points)
        {

            if (right < p[0])
            {
                n++;
                right = p[1];
            }
            else
            {
                right = min(right, p[1]);
            }
        }
        return n;
    }
};

void mytest02()
{

    Solution2 s2;
    // vector> points = {{3, 9}, {7, 12}, {3, 8}, {6, 8}, {9, 10}, {2, 9}, {0, 9}, {3, 9}, {0, 6}, {2, 8}};
    vector> points = {{9, 12}, {1, 10}, {4, 11}, {8, 12}, {3, 9}, {6, 9}, {6, 7}};

    // vector> points = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
    auto res = s2.findMinArrowShots(points);
    cout << res << endl;

    cout << endl;
}

/*
买卖股票的最佳时机。122. 买卖股票的最佳时机 IIhttps://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。

示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。
示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。
示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

贪心算法的直觉:由于不限制交易次数,只要今天股价比昨天高,就交易。

下面对这个算法进行几点说明:

该算法仅可以用于计算,但 计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明等价性:以 [1, 2, 3, 4] 为例,这 4 天的股价依次上升,按照贪心算法,得到的最大利润是:

res =  (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])
    =  prices[3] - prices[0]

仔细观察上面的式子,按照贪心算法,在下标为 1、2、3 的这三天,我们做的操作应该是买进昨天的,卖出今天的,虽然这种操作题目并不允许,但是它等价于:在下标为 0 的那一天买入,在下标为 3 的那一天卖出。

*/

// class Solution3
// {
// public:
//     int maxProfit(vector &prices)
//     {

//         int n = prices.size();
//         int ans = 0;
//         for (int i = 1; i < n; i++)
//         {

//             ans += std::max(prices[i] - prices[i - 1], 0);
//         }

//         return ans;
//     }
// };

// void mytest03()
// {

//     Solution3 s3;
//     vector prices = {7, 1, 5, 3, 6, 4};
//     auto res = s3.maxProfit(prices);
//     cout << res << endl;
// }

// int main()
// {
//     // mytest01();
//     // mytest02();
//     mytest03();

//     system("pause");
//     return 0;
// }

//

/*
leetcode 605 

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,
能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false 。



示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
示例 2:

输入:flowerbed = [1,0,0,0,1], n = 2
输出:false



*/


class Solution {
public:
    bool canPlaceFlowers(vector& flowerbed, int n) {

    }
};

void mytest04(){





}


int main(){




    system("pause");
    return 0;
}

//橡皮泥问题。

/*

酷酷的小明准备和小伙伴们展示他捏出来的超酷的橡皮泥士兵。在展示之前,小明发现有些像皮泥士兵大小十分相似甚至相同,这让小明感觉不是很酷,因为小明想要他的像皮泥作品都有自己的风格,
即使是大小也要有区别。小明的n个橡皮泥士兵的大小分别为a1,a2.an,小明可以通过给某个士兵加一单位橡皮泥来使得其大小增加一单位。小明想知道如果他想要让所有的像皮泥士兵大小都不相同,
至少需要一共加多少单位橡皮泥?第一行1个整数n,表示小明的橡皮泥士兵数量
,分别表示小明的橡皮泥士兵的大小
第二行n个整数a1 a2...an!对于100%的数据,1ns50000,1
#include 
#include 

using namespace std;

int main()
{
    int n;
    cin >> n;

    vector v(n);
    for (int i = 0; i < n; i++)
    {
        cin >> v[i];
    }

    set uniqueSizes;
    int addition = 0;

    for (int i = 0; i < n; i++)
    {
        while (uniqueSizes.count(v[i]) > 0)
        {
            v[i]++;
            addition++;
        }
        uniqueSizes.insert(v[i]);
    }

    cout << addition << endl;

    system("pause");
    return 0;
}






你可能感兴趣的:(算法,贪心算法,leetcode)