C/C++语言算法篇(一):贪心算法

贪心算法

正所谓人人都有贪心,C语言算法上的贪心可不是实际意义上的贪心,C语言结构上的贪 心可以说满足两个条件:贪心选择性质和最优子结构性质。满足这两个条件的话就可以尝试用贪心算法解决问题。
贪心选择性质是指原问题的整体最优解可以通过一系列局部最优的选择得到。
应用同一规则,将原问题变为一个相似的但规模更小的子问题,而后的每一步都是当前最佳的选择。这种选择依赖于已做出的选择,但不依赖于未做出的选择。运用贪心策略解决的问题在程序的运行过程中无回溯过程。
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题是否可用贪心算法求解的关键。例如原问题S={a1,
a2,…,ai,…, an},通过贪心选择选出一个当前最优解{ai}之后,转化为求解子问题
S-{ai},如果原问题的最优解包含子问题的最优解,则说明该问题满足最优子结构性质,

算法步骤

1、 选择算法结构

就是说你必须要选择一个贪心的方案,就好比如你去买菜,什么菜最好,什么菜买了才不会吃亏(这又关于另一个问题,物品的价值,越高越好)。当然啦,你愿意当个大傻个让别人扎一刀,那也无所谓的。

2、 局部最优解

这里说的呢就是实战的过程最优解,就好比如你要买一袋苹果,一般来说肯定拿最大的先(这里就是一个局部最优解),接着就是摊主上第二大的苹果,以此类推,最终的出的结果就是贪心的后果咯。

3、 算法实战

1、船装载问题

有一条船的装载量一定,要求装载的物品的数量尽可能多, 而船的容量是固定的, 那么优先把重量小的物品放进去,
在容量固定的情况下,装的物品最多。采用重量最轻者先装的贪心选择策略,从局部最优达到全局最优,从而产生最优装载问题的最优解。 (
1)当载重量为定值 c 时, wi 越小时,可装载的古董数量 n 越大。只要依次选择最小重量古董,直到不能再装为止。 ( 2)把 n
个古董的重量从小到大(非递减)排序,然后根据贪心策略尽可能多地选出前i 个古董,直到不能继续装为止,此时达到最优。

源代码

#include 
#include 
const int N = 1000005;
using namespace std;
double w[N]; //古董的重量数组
int main()
{
double c;
int n;
cout<<"请输入载重量 c 及古董个数 n: "<<endl;
cin>>c>>n;
cout<<"请输入每个古董的重量,用空格分开: "<<endl;
for(int i=0;i<n;i++)
{
cin>>w[i]; //输入每个物品重量
}
sort(w,w+n); //按古董重量升序排序
double temp=0.0;
int ans=0; // tmp 为已装载到船上的古董重量, ans 为已装载的古董个数
for(int i=0;i<n;i++)
{
tmp+=w[i];
if(tmp<=c)
ans ++;
else
break;
}
cout<<"能装入的古董最大数量为 Ans=";
cout<<ans<<endl;
return 0;
}

2、背包问题

山洞中有 n 种宝物,每种宝物有一定重量 w 和相应的价值 v,毛驴运载能力有限, 只能运走 m
重量的宝物,一种宝物只能拿一样,宝物可以分割。那么怎么才能使毛驴运走宝物的价值最大呢 (
1)每次挑选价值最大的宝物装入背包,得到的结果是否最优? ( 2)每次挑选重量最小的宝物装入,能否得到最优解? (
3)每次选取单位重量价值最大的宝物,能否使价值最高? 未了满足我们人的小小贪心,就是选择第三种啦。性价比最高,所得到的的最后总价值最大。

源代码

#include
#include
using namespace std;
const int M=1000005;
struct three{
double w;//每个宝物的重量
double v;//每个宝物的价值
double p;//性价比
}s[M];
bool cmp(three a,three b)
{
return a.p>b.p;//根据宝物的单位价值从大到小排序
}
int main()
{
int n;//n 表示有 n 个宝物
double m ;//m 表示毛驴的承载能力
cout<<"请输入宝物数量 n 及毛驴的承载能力 m : "<<endl;
cin>>n>>m;
cout<<"请输入每个宝物的重量和价值,用空格分开: "<<endl;
for(int i=0;i<n;i++)
{
cin>>s[i].w>>s[i].v;
s[i].p=s[i].v/s[i].w;//每个宝物单位价值
}
sort(s,s+n,cmp);
double sum=0.0;// sum 表示贪心记录运走宝物的价值之和
for(int i=0;i<n;i++)//按照排好的顺序贪心
{
if( m>s[i].w )//如果宝物的重量小于毛驴剩下的承载能力
{
m-=s[i].w;
sum+=s[i].v;
}
else//如果宝物的重量大于毛驴剩下的承载能力
{
sum+=m*s[i].p;//部分装入
break;
}
}
cout<<"装入宝物的最大价值 Maximum value="<<sum<<endl;
return 0;
}

4、 会议安排问题

会议安排的目的是能在有限的时间内召开更多的会议(任何两个会议不能同时进行)。在会议安排中,每个会议 i 都有起始时间 bi 和结束时间
ei,且 bi ej)均在“有限的时间内”,且不相交,则称会议 i 与会议 j 相容的。也就是说,当 bi≥ej 或 bj≥ei 时,会议 i与会议 j
相容。会议安排问题要求在所给的会议集合中选出最大的相容活动子集,即尽可能在有限的时间内召开更多的会议。总的来说就是了解各个会议的开始时间和结束时间,按照时间安排,使会议能举办的最多。
会议时间表 会议 i 1 2 3 4 5 6 7 8 9 10 开始时间 bi 8 9 10 11 13
14 15 17 18 16 结束时间 ei 10 11 15 14 16 17 17 18 20 19 (
1)每次从剩下未安排的会议中选择会议具有最早开始时间且与已安排的会议相容的会 议安排,以增大时间资源的利用率。 (
2)每次从剩下未安排的会议中选择持续时间最短且与已安排的会议相容的会议安排, 这样可以安排更多一些的会议。 (
3)每次从剩下未安排的会议中选择具有最早结束时间且与已安排的会议相容的会议安排,这样可以尽快安排下一个会议。

源代码

#include 
#include 
#include 
using namespace std;
struct Meet
{
int beg; //会议的开始时间
int end; //会议的结束时间
int num; //记录会议的编号
}meet[1000]; //会议的最大个数为 1000
class setMeet{
public:
void init();
void solve();
private:
int n,ans; // n:会议总数 ans: 最大的安排会议总数
};
//读入数据
void setMeet::init()
{
int s,e;
cout <<"输入会议总数: "<<endl;
cin >> n;
int i;
cout <<"输入会议的开始时间和结束时间,以空格分开: "<<endl;
for(i=0;i<n;++i)
{
cin>>s>>e;
meet[i].beg=s;
meet[i].end=e;
meet[i].num=i+1;
}
}
bool cmp(Meet x,Meet y)
{
if (x.end == y.end)
return x.beg > y.beg;
return x.end < y.end;
}
void setMeet::solve()
{
sort(meet,meet+n,cmp); //对会议按结束时间排序
cout <<"排完序的会议时间如下: "<<endl;
int i;
cout <<"会议编号"<<" 开始时间 "<<" 结束时间"<<endl;
for(i=0; i<n;i++)
{
cout<< " " << meet[i].num<<"\t\t"<<meet[i].beg <<"\t"<< meet[i].end << endl;
}
cout <<"-------------------------------------------------"<<endl;
cout << "选择的会议的过程: " <<endl;
cout <<" 选择第"<< meet[0].num<<"个会议" << endl;//选中了第一个会议
ans=1;
int last = meet[0].end; //记录刚刚被选中会议的结束时间
for( i = 1;i < n;++i)
{
if(meet[i].beg>=last)
{ //如果会议 i 开始时间大于等于最后一个选中的会议的结束时间
ans++;
last = meet[i].end;
cout <<" 选择第"<<meet[i].num<<"个会议"<<endl;
}
}
cout <<"最多可以安排" <<ans << "个会议"<<endl;
}
int main()
{
setMeet sm;
sm.init();//读入数据
sm.solve();//贪心算法求解
return 0;
}

你可能感兴趣的:(C/C++语言算法篇(一):贪心算法)