贪心

贪心

简单贪心

贪心法是求解一类最优化问题的方法,它总是考虑在当前状态下局部最优的策略,以使全局最优

思路是: 从问题的某个初始解出发一步一步地进行,根据某个优化测度每一步都要确保获得局部最优解. 如果下一个数据和部分最优解连在一起不再是可行解时, 就不把该数据添加到部分解中, 直到把所有数据枚举完, 或者不能再添加算法停止, 这样找到的第一个解通常就是最优的

PAT B1020月饼/PAT A1070Mooncake

image-20210315165038926

题意

已知月饼需求量D, n种月饼各自的总售价和库存量, 问怎样销售这些月饼, 使总销售额达到最大

思路

总是选择单价最高的月饼出售

先计算出各种月饼的单价, 再按从大到小排序枚举, 直到需求量被满足

实现
#include<bits/stdc++.h>
using namespace::std;
struct moon
{
    double n;   //库存量
    double price;   //单价
    double total;   //总价
};

bool cmp(moon a, moon b)
{
    return a.price > b.price;   //使用sort后是由大到小排序
}

int main()
{
    moon cake[1005];
    int n;
    double d, sum = 0;
    cin >> n >> d;
    for(int i = 1; i <= n; i++)
        cin >> cake[i].n;
    for(int i = 1; i <= n; i++)
        cin >> cake[i].total;
    for(int i = 1; i <= n; i++)
        cake[i].price = cake[i].total / cake[i].n;
    sort(cake + 1, cake + n + 1, cmp);
    for(int i = 1; i <= n; ++i)
    {
        if(d >= cake[i].n)
        {
            sum += cake[i].total;
            d -= cake[i].n;
        }
        else
        {
            sum += cake[i].price * d;
            break;
        }
    }
    printf("%.2f", sum);
    return 0;
}

PAT B1023组个最小数

image-20210315185925125

思路

先从1~9中选出一个不为0的最小数输出, 再从小到大依次输出各个数字

实现
#include <bits/stdc++.h>
int main()
{
    int count[15];
    for(int i = 0; i <= 9; i++)
        cin >> count[i];
    for(int i = 1; i <= 9; i++)
    {
        if(count[i] != 0)
        {
            cout << i;
            count[i]--;
            break;
        }
    }
    for(int i = 0; i <= 9; ++i)
        for(int j = 1; j <= count[i]; ++j)
            cout << i;
    
    return 0;
}

LanQiao基础训练-完美的代价

image-20210315201106949

思路

首先判断Impossible的情况

回文串, 需要左右对称, 只有最中间的那个字符不需要有和它相同的字符对应(当然,这是串长为奇数的情况), 这说明奇数个的字符种类 > 1就必无法构成回文串, 例如abbbaa

下面进行字母交换 (注意只有相邻的字母才能交换)

保证最小次数的策略: 每个字母都单向移动 (不做无用功), 从外向内进行交换, 内部的交换不会影响外部, 不易产生重复

在串中点的左边枚举一个字符, 在该字符的右边找一个和它相同的字符完成交换(由外向内查找), 记录次数, 单个找不到相同字母的字符不进行交换, 直接计算其到达中点需要走的步数即可

实现
#include <bits/stdc++.h>
using namespace::std;
char str[9000];
int st[30];
int main()
{
    int n;
    cin >> n;
    cin >> str + 1;

    //判断Impossible的情况
    for(int i = 1; i <= n; ++i)
    {
        int temp = str[i] - 96;
        st[temp]++;
    }
    int cnt = 0;
    for(int i = 1; i <= 26; ++i)
    {
        if(st[i] % 2 != 0)
            cnt++;
    }
    if(cnt > 1)
    {
        cout << "Impossible";
        return 0;
    }

    //通过交换构成回文
    int pos = n;    //需要构成回文的位置
    int ans = 0;    //计数
    for(int i = 1; i < (n + 1) / 2; ++i)
    {
        if(str[i] == str[pos])
        {
            pos--;    //该位置可以直接构成回文,需要构成回文的位置向里走
            continue;
        }
        else    //不能构成回文,需要进行交换
        {
            int j;
            for(j = pos - 1; j > i; --j)  //查找相同的字母
            {
                if(str[j] == str[i])
                {
                    while(j < pos)
                    {
                        char temp = str[j];
                        str[j] = str[j + 1];
                        str[j + 1] = temp;
                        ans++;
                        j++;
                    }
                    pos--;    //通过交换构成了回文,需要构成回文的位置向里走
                    break;  //已构成回文,停止查找
                }
            }
            if(i == j)  //没有找到相同的字符
            {
                ans += (n + 1) / 2 - i; //单个的字母走到中间需要交换的次数
            }
        }
    }
    cout << ans;
    return 0;
}

区间贪心

引例: 区间不相交问题

给出n个开区间(x, y), 从中选择尽量多的开区间, 使得这些开区间两两没有交集

例如对于开区间(1, 3) (2, 4) (3, 5) (6, 7), 选出最多三个区间(1, 3) (3, 5) (6, 7)

思路
  1. 区间A为区间B包含的情况, 例如A = (1, 9)B = (3, 4), 此时, 显然选择B, 更有利于容纳更多区间
  2. 接下来将所有开区间按左端点从大到小排列, (设区间A1的左端点为x1, 右端点为y1, 其余区间同理), 排除包含区间的情况后, 必有y1 > y2 > ... > yn, 如果将A1的右端和A2不重合部分去掉, 那么A1就会被A2包含, 根据1中的结论, 应当选择A1, 推而广之, 也就是我们总是选左边端点大的区间

image-20210316113228969

实现
#include<bits/stdc++.h>
using namespace::std;
struct Interval //区间
{
    int x;  //左端点
    int y;  //右端点
};

bool cmp(Interval a1, Interval a2)
{
    return a1.x >= a2.x;
}

int main()
{
    int n, ans = 1;
    cin >> n;
    Interval a[1000];
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i].x >> a[i].y;
    }
    sort(a + 1, a + n + 1, cmp);
    prex = a[1].x;
    for(int i = 2; i <= n; ++i)
    {
        if(a[i].y <= prex)
        {
            ans++;
            prex = a[i].x;
        }
    }
    cout << ans;
    return 0;
}

显然, 贪心算法适用于全局的最优解可以由子问题的最优解得出的问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值