目录
- 贪心法的概念
- 贪心法的性质
- 活动安排问题
- 乘船问题
- 选择不相交区间
- 背包问题
贪心法的概念
贪心算法是一种求解组合优化问题的算法设计技术,其求解过程由一系列决策构成每一步决策仅依赖于某种局部优化的性质。
和动态规划算法不同,贪心算法在做决策时候不必考虑所有子问题的选择结果。
贪心法的性质
- 贪心选择性质
- 最优子结构性质
贪心法的适用条件
问题的求解可以由一系列的决策步骤构成,每步决策依赖于某种局部最优的贪心策略。 保证每一步基于局部优化性质的选择最终导致全局的最优解。
主要设计步骤
- 将问题的求解看作是一系列的决策;
- 确定每一步决策所依据的局部优化性质;
- 证明每一步基于局部优化性质的选择最终导致全局最优解.
贪心法主要的难点在于证明其正确性,可以使用数学归纳法证明贪心法的正确性。
活动安排问题
题目链接
解题代码
#include <bits/stdc++.h>
const int maxn = 10000 + 10;
using namespace std;
struct Node{
int l,r;
};
int cmp(Node a,Node b){
return a.r < b.r;
}
int main(){
int T,n;
Node node[maxn];
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i = 0; i < n; i++)scanf("%d%d",&node[i].l,&node[i].r);
sort(node,node+n,cmp);
int End = node[0].r;
int sum = 1;
for(int i = 1; i < n; i++){
if(node[i].l > End){
sum++;
End = node[i].r;
}
}
printf("%d\n",sum);
}
return 0;
}
乘船问题
题目描述
有n个人,第i个人重量为wi。每艘船的最大载重量均为C,且最多只能乘两个人。用最少的船装载所有人。
解析
首先从最轻的人开始考虑,那么他应该和最重的人去坐(贪心选择,为后面节省更多空间),如果每个人都不能和它坐船,那么唯一的方法就是每个人做一艘船。
否则,他应该选择能够和他一起坐船的人中最重的那个,这样子才不会浪费。
反证法的两点证明:
- 假设i不与任何人同船,把j(或者比j轻的人)来同船会减少船的数量。
- 假设i与k同船,因为j是与i匹配的最重的,所以w(k)<=w(j),则j加入其它船可能会使其它船超重,用的船数会变多;
#include <bits/stdc++.h>
const int maxn = 100 + 10;
using namespace std;
int cmp(const void *a,const void *b){
return *(int*)a - *(int*)b;
}
int main(){
//freopen("in.txt","r",stdin);
int w[maxn],n,C;
while(scanf("%d%d",&C,&n) != EOF){
for(int i = 1; i <= n; i++)scanf("%d",&w[i]);
//sort(w+1,w+1+n); //排序
qsort(w,n+1,sizeof(w[1]),cmp); //数组,最后的下标加一,开始下标
int i = 1, j = n;
int ans = 0;
while( i <= j){
if( i == j){ ans++; break; } //当相等的时候也就意味着其他的配对了,只剩自己了
if(w[i] + w[j] <= C){ //如果可以配对
ans++;
i++;
j--;
}else{ //不能配对 -->只能重的人做一艘船
ans++;
j--;
}
}
printf("%d\n",ans);
}
return 0;
}
测试数据
测试数据:
85 6
5 84 85 80 84 83
90 3
90 45 60
100 5
50 50 90 40 60
输出:
5
3
3
选择不相交区间
题目
就是给一系列的区间,求最多的区间,要求区间个数最多,这些区间不相交,需要注意的是这些区间都是闭区间。
题目链接
解题思路
也是贪心选择,按照区间右端点排序。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 10;
struct Node{
int x,y;
bool operator < (const Node & rhs) const{
return y < rhs.y || (y == rhs.y && x < rhs.x);
}
}node[maxn];
int main(){
//freopen("in.txt","r",stdin);
int n,kase = 0;
int path[maxn]; //记录区间
while(scanf("%d",&n) != EOF){
for(int i = 0; i < n; i++){
scanf("%d%d",&node[i].x,&node[i].y);
if(node[i].x > node[i].y)swap(node[i].x,node[i].y); //注意这里要交换一下
}
sort(node,node+n);
int End = node[0].y; //先选择第一个
int sum = 1; //记录个数,第一个必须选
for(int i = 1; i < n; i++){
if(node[i].x > End){
path[sum++] = i;
End = node[i].y;
}
}
//printf("%d %d\n",node[0].x,node[0].y);
//for(int i = 1; i < sum; i++)printf("%d %d\n",node[path[i]].x,node[path[i]].y); //输出区间
printf("Case %d:\n",++kase);
printf("%d.\n",sum);
}
return 0;
}
背包问题
背包问题和01背包问题不同,其差别在于每个物品可以放入一部分,这样的话,我们贪心选择单位重量价值依此从高到底的物品,最后一个物品能装多少就装多少
01背包问题用贪心算法得不到最优解的原因在于无法保证将背包装满,部分闲置的背包空间使得每千克背包空间的价值降低了。
#include <bits/stdc++.h>
const int maxn = 100 + 10;
using namespace std;
double x[maxn];
struct Kanpsack{
int w,v;
bool operator < (const Kanpsack& rhs){
return v*1.0/w > rhs.v*1.0/rhs.w;
}
};
int main(){
//freopen("in.txt","r",stdin);
int n,C;
scanf("%d %d",&n,&C);
Kanpsack kp[maxn];
for(int i = 0; i < n; i++)scanf("%d %d",&kp[i].w,&kp[i].v);
sort(kp,kp+n);
for(int i = 0; i < n; i++)x[i] = 0;
int now = C,i;
for(i = 0; i < n; i++){
if(kp[i].w > now)break;
x[i] = 1;
now -= kp[i].w;
}
if(i <= n-1)x[i] = now*1.0/kp[i].w; //把最后一个能装多少装多少
for(int i = 0; i < n; i++)printf("%lf ",x[i]);
printf("\n");
return 0;
}
测试数据
3 50
10 60
20 100
30 120
输出
1.000000 1.000000 0.666667