左神课堂之贪心题目

题目一:

题意:

一块金条切成两半,是需要花费和长度数值相同的铜板的,比如长度为20的金条,不管切成长度为多大的两半,都需要花费20个铜板,一群人想整分整块金条,怎么分最省铜板?

例如:给定数组【10,20,30】,代表一共三个人,整块金条的长度为10+20+30=60,金条要分成10,20,30三个部分。如果,先把长度为60的金条分成10和50,花费60,在办长度为50的金条分成20和30,花费50,一共花费110铜板。
但是如果先把长度为60的金条分成30和30的,在把长度为30的金条分成10和20,一共花费90铜板。
输入一个数组,输出分割最小代价。

思路:

利用哈夫曼编码,每次选取数组中最小和次小的两个元素进行合并,然后把合并后的数字重新放入数组中去,然后重复进行上述过程,直到数组中只有一个数字时停止。(和找最优二叉树的算法一模一样------哈夫曼算法)

上述过程完成后可以生成一个二叉树,我们以【10,20,30】举例:

数据结构:

优先级队列

代码如下:

#include<bits/stdc++.h>
#define MAXN 99999
using namespace std;
int root[MAXN];//保存原始信息
int n;
int sum=0;//结果,累加二叉树中所有的非叶节点
class node
{
private:
    priority_queue< int , vector<int> , greater<int> > q;//定义一个从递增的优先级队列
public:
    node()//构造函数,往优先级队列中添加原始数据
    {
        for(int i=0;i<n;i++)
            q.push(root[i]);
    }
    int heart()//核心函数
    {
        while (q.size()!=1)//当优先级队列中只有一个元素时,说明没法和了,退出
        {
            int nowday1=q.top();//取出数据中最小的和次小的元素,和成一个新元素,添加到优先级队列中去
            q.pop();
            int nowday2=q.top();
            q.pop();
            int nowday3=nowday1+nowday2;
            q.push(nowday3);
            sum+=nowday3;//累加所有非叶节点
        }
        return sum;
    }
};
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>root[i];
    node node1;
    cout<<node1.heart()<<endl;
}

题目二:

题意:

有一些项目,每个项目都有成本和利润,现在给定n个项目的成本和利润、拥有的本金(corpus)和最多可以做的项目的个数(k),让你合理选择项目,使得做完这些项目后你手中所拥有的钱数最多。

 

题目分析:

运用贪心策略,每次都做能够做的利润最高的项目。定义二个优先级队列。一个是按照成本升序的优先级队列:minq,另一个是

按照利润降序的优先级队列:maxq。首先将所有的项目(附带在数组中的索引)放入minq中去----->将所有的成本小于等于本金的项目放入到maxq中去------>从maxq中拿出队头元素,做这个项目(做完之后本金(corpus)增加了)---->再次查看minq中哪些项目可以进入到maxq中去----->直到做完k个项目后或者没有项目可以做时,算法结束。

数据结构:

升序的优先级队列

降序的优先级队列

代码如下:

#include<bits/stdc++.h>

#define MAXN 99999
using namespace std;
int cost[MAXN];//成本数组
int profit[MAXN];//利润数组    成本数组和利润数组是一一对应的
int n;//项目的个数
int corpus;//本金
int k;//最多能够做的项目
class minqueue//小根堆的数据结构
{
public:
    int cost1;//成本
    int i;//在成本(利润)数组中索引
    friend bool operator < (minqueue a1,minqueue a2)//按成本从小到大排序
    {
        return a1.cost1>a2.cost1;
    }
    minqueue(int cost1,int i):cost1(cost1),i(i){};
};
class maxqueue//大根堆的数据结构
{
public:
    int profit1;//利润
    int i;//在成本(利润)数组中索引
    friend bool operator < (maxqueue a1,maxqueue a2)//按利润从大到小排序
    {
        return a1.profit1<a2.profit1;
    }
    maxqueue(int profit1,int i):profit1(profit1),i(i){};
};
class node
{
private:
    priority_queue< minqueue > minq;//建立一个以成本为标准的递增的优先级数列,存储成本和索引(小根堆)
    priority_queue< maxqueue > maxq;//建立一个以利润为标准的递减的优先级队列,存储利润和索引(大根堆)
    void into_the_maxqueue()//从小根堆中拿出成本小于等于当前资金的项目放到大根堆中去
    {
        if(minq.empty())//小根堆中没有项目了,直接返回
            return ;
        minqueue nowdayminqueue=minq.top();
        while (nowdayminqueue.cost1<=corpus)//只要当前的项目的成本小于等于当前资金就放入到大根堆中去
        {
            minq.pop();
            maxq.push(maxqueue(profit[nowdayminqueue.i],nowdayminqueue.i));
            if(!minq.empty())//这个条件满足,说明还有项目可以比较,如果没有项目可以比较了,就直接退出while循环
                nowdayminqueue=minq.top();
            else
                break;
        }
    }
public:
    node()//刚开始把所有的项目放到小根堆中去,并进行一次 into_the_maxqueue() 操作
    {
        for(int i=0;i<n;i++)
            minq.push(minqueue(cost[i],i));
        into_the_maxqueue();
    }
    int heart()
    {
        maxqueue nowdaymaxqueue=maxq.top();
        maxq.pop();
        while (k--)
        {
            corpus+=nowdaymaxqueue.profit1;//这条语句执行完成,说明这个项目就做完了
            into_the_maxqueue();//每做完一个项目,就得进行一次 into_the_maxqueue() 操作,因为corpus(当前资金)增加了,以前不能够做的项目有可能能做了
            if(!maxq.empty())//只要大根堆中没有项目了,就直接结束就行了
            {
                nowdaymaxqueue=maxq.top();
                maxq.pop();
            }
            else
                break;
        }
        return corpus;
    }
};
int main()
{
    cin>>n>>corpus>>k;
    for(int i=0;i<n;i++)
        cin>>cost[i]>>profit[i];
    node node1;
    cout<<node1.heart()<<endl;
}

题目三:

题意:


给定一些字符串,让你通过一定的算法来将这些字符串拼接起来,使拼接后的字符串字典序最小。
我们假设给定的字符串是:abfgh和kgegr,我们有2种拼接方式,第一种:abfghkgegr;第二种:kgegrabfgh。这两种拼接方式我们选择第一种,因为第一种所拼接出来的字符串的字典序小。

题目分析:

我们可以设定一种贪心策略:将所有的字符串按从小到大排序,然后拼接起来。但是这种贪心策略是不对的,我们举个例子:假设有2个字符串:b,ba。我们按从小到大排序然后拼接得到的字符串是bba,但是这个字符串不是最小的,还有更小的就是bab,所以这种贪心策略是不对的。

假设另一种贪心策略:给定两个字符串a,b,如果a+b得到的字符串小于b+a得到的字符串,那么就把a排在前面,b排在后面

否则,b放在前面,a放在后面。

代码实现:(我的水平有限,c++中我的代码可以编译,但是打出来的不是想要的字符串,但是思路都在代码中体现了)

#include<bits/stdc++.h>
#define MAXN 9999
using namespace std;
string root[MAXN];
int n;
bool cmp(string a,string b)//构造比较器
{
        return a+b<b+a;
}
class node
{
public:
    string loweststring()//核心函数
    {
        sort(root,root+n,cmp);
        string ret=NULL;
        for(int i=0;i<n;i++)
            ret+=root[i];
        return ret;
    }
};
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>root[i];
    node node1;
    cout<<node1.loweststring()<<endl;
}

题目四:

题目描述:

一些项目要占用会议室宣讲,会议室不能同时容纳两个项目的宣讲,给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次

题目分析:

我们按照每个项目的结束时间来进行贪心,哪个项目的结束时间早,我们就选哪个项目。定义一个以结束时间升序的优先级队列,每次从队顶中拿出一个活动,用该活动的开始时间和上一个选择了的活动的结束时间进行比较,如果满足条件,就说明能够安排这个活动。

代码如下:

#include<bits/stdc++.h>
#define MAXN 99999
using namespace std;
int n;//活动个数
int number=0;//结果
class project//此题所用到的数据结构
{
public:
    int start;//当前活动的开始时间
    int end;//当前活动的结束时间
    friend bool operator < (project a,project b)//以结束时间递增的规则定义一个比较器
    {
        return a.end>b.end;
    }
    project(int start,int end):start(start),end(end){};
};
class node
{
private:
    priority_queue< project >q;//优先级队列
public:
    node()//构造函数,往优先级队列中加入原始数据
    {
        int nowdaystar,nowdayend;
        for(int j=0;j<n;j++)
        {
            cin>>nowdaystar>>nowdayend;
            q.push(project(nowdaystar,nowdayend));
        }
    }
    void heart()//核心函数
    {
        if(q.empty())
            return ;
        int nowdayend1=0;//上一个可选活动的结束时间
        while (!q.empty())//只要队列不是空,就继续进行
        {
            project nowdayproject=q.top();
            q.pop();
            if(number==0||nowdayproject.start>=nowdayend1)//条件一:number==0是刚开始时一个活动也没有选的情况,条件二:当前活动的开始时间在上一个选择了的活动的结束时间的后面
            {
                number++;//活动个数加一
                nowdayend1=nowdayproject.end;//记录这个活动的结束时间,用于判断下一个活动是否可以选择
            }
        }
    }
};
int main()
{
    cin>>n;
    node node1;
    node1.heart();
    cout<<number<<endl;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值