题目一:
题意:
一块金条切成两半,是需要花费和长度数值相同的铜板的,比如长度为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 99999using 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;
}