贪心算法题目分类总结


贪心算法


1.贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
2.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。


基本思路


  1. 建立数学模型来描述问题;
  2. 把求解的问题分成若干个子问题;
  3. 对每一子问题求解,得到子问题的局部最优解;
  4. 把子问题的解局部最优解合成原来解问题的一个解。

例题分析


.背包问题

题意
有一个背包,背包容量是M。有n个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。

思路

  1. 计算出每个物品单位重量的价值
  2. 按单位价值从大到小将物品排序
  3. 根据背包当前所剩容量选取物品
  4. 如果背包的容量大于当前物品的重量,那么就将当前物品装进去。否则,那么就将当前物品舍去,然后跳出循环结束

代码

#include<iostream>
#include<algorithm>
using namespace std;
typedef struct{
    int w;
    int v;
    double avg;
}P;
bool cmp(P a,P b){
    return a.avg>b.avg;
}
int main(){
    P *p;
    int n,i,m;//n 物品个数 m背包容量
    while(cin>>n>>m){
        p=new P[n];
        for(i=0;i<n;i++){
            cin>>p[i].w>>p[i].v;
            p[i].avg=p[i].v/p[i].w*1.0;
        }
        sort(p,p+n,cmp);
        int maxvalue=0;
        for(i=0;i<n;i++){
            if(p[i].w<=m){
                m-=p[i].w;
                maxvalue+=p[i].v;
            }else{
                break;
            }
        }
        cout<<maxvalue<<endl;
    }
    return 0;
}

.活动安排问题

题意
输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。

Sample Input

12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0

Sample Output

5

思路
要求看的节目数量最多,从而可使用贪心的算法尽可能多看,首先是将数据按照节目结束时间从小到大排序,越是结束时间早,越是可以屯出时间去看下一个节目,看完一个节目后,检查排序后相邻的下一个节目的start是否大于或者等于当前节目的end,如果大成立则下一个节目是下次准备看的节目,否则再往下继续找,直到满足条件为止

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct time
{
    int t1;
    int t2;
}a[110];
int cmp(time a,time b)
{
    if(a.t2==b.t2)
        return a.t1>b.t1;
    return a.t2<b.t2;
}
int main()
{
    int n,s,t,i;
    t=0;
    while((cin>>n)&&n!=0)
    {
        for(i=0; i<n; i++)
        {
            cin>>a[i].t1>>a[i].t2;
        }
        sort(a,a+n,cmp);
        for(i=1,t=a[0].t2,s=1; i<n; i++)
        {
            if(a[i].t1>=t)
            {
                t=a[i].t2;
                s++;
            }
        }
        cout<<s<<endl;
    }
    return 0;
}

.考虑卖商品截止日期求最大利润

题意
超市里有n个产品要卖,每个产品都有一个截至时间dx(从开始卖时算起),只有在这个截至时间之前才能卖出并且获得率润dy。
有多个产品,所有可以有不同的卖出顺序,每卖一个产品要占用1个单位的时间,问最多能卖出多少利润。

Sample Input

4  50 2  10 1   20 2   30 1

7  20 1   2 1   10 3  100 2   8 2
   5 20  50 10

Sample Output

80
185

思路
先把所有产品按照利润从大到小排序,然后这个把这个放在截止日期那天卖出,并做好标记,如果截至日期那天已经有其他产品占用了,那么可以把这个产品卖出的时间往前推,直到找到可以卖的那一天并标记好。
假设一个产品a占用了一个日期后,那么如果下次又有一个产品b和产品a的截止日期是相同的,但是那个日期以被占用了,所以就要往前移动1天,那么就可以用并查集进行标记,在a占用了那个日期后,把a的截止日期指向前一个日期,这样的话,可以直接查找到他要占用到哪一个时间。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 using namespace std;
 typedef long long ll;
const int maxn=10005;
int visited[maxn]; //代表第i天是否有卖商品
 struct node{
    int p,d;
};
 bool cmp(node a,node b){
    return a.p>b.p;
}
 int main()
{
    int n;
    while(~scanf("%d",&n)){
        memset(visited,0,sizeof(visited));
        node a[maxn]; //存商品
        for(int i=0;i<n;i++){
            scanf("%d%d",&a[i].p,&a[i].d);
        }
        sort(a,a+n,cmp);
        int sum=0;
        for(int i=0;i<n;i++){
            for(int j=a[i].d;j>=1;j--){ //在截止日期之前卖出就行
                if(visited[j])
                    continue;
                visited[j]=1;
                sum+=a[i].p;
                break;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

.区间覆盖问题

题意
铺路问题。给出n段长度的起止位置,要求用长为l的木板铺路,最少用多少块木板可以铺完。

Sample Input

3 3
1 6
13 17
8 12

Sample Output

5

思路
既然给出n个泥坑的起止位置,而且不重合,那么就按照开始的位置进行排序。
排序后从头开始铺木板。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define MAX 100000
using namespace std;
struct DIS
{
    int s,e;
}dis[MAX];
bool cmp(DIS a,DIS b)
{
    return a.s < b.s;
}
int main()
{
    int n,l;
    scanf("%d%d",&n,&l);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d",&dis[i].s,&dis[i].e);
    }
    sort(dis + 1, dis + 1 + n,cmp);
    int sum = 0;//sum表示总共需要的木板
    int length = 0;
    for (int i = 1; i <= n; i++)
    {
        if (length >= dis[i].e)//如果当前长度大于泥坑的末端,则上一块木板可以铺完,continue;
            continue;
        length = max(dis[i].s,length);//否则看是否铺开新的泥坑。即比较当前长度和下一个泥坑开始的长度。
        while (length < dis[i].e)//这里就是铺下一个泥坑,直到当前长度铺完当前长度的泥坑。
        {
            sum += 1;
            length += l;
        }
    }
    printf("%d\n",sum);
    return 0;
}

.多机调度问题

1.机器是完全相同的,不同的作业处理时间是独立的
题意
n个作业组成的作业集,可由m台相同机器加工处理。要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。

思路
作业不能拆分成更小的子作业;每个作业均可在任何一台机器上加工处理。 这个问题是NP完全问题,还没有有效的解法(求最优解),但是可以用贪心选择策略设计出较好的近似算法(求次优解)。当n<=m时,只要将作业时间区间分配给作业即可;当n>m时,首先将n个作业从大到小排序,然后依此顺序将作业分配给空闲的处理机。也就是说从剩下的作业中,选择需要处理时间最长的,把它分配给当前总累计需要工作时长最短的机器。

代码

#include<iostream>    
#include<algorithm>      
using namespace std;    
int speed[10010];    
int mintime[110];    
bool cmp( const int &x,const int &y)    
{    
    return x>y;    
}    
 int main()    
{    
    int n,m;           
    memset(speed,0,sizeof(speed));    
    memset(mintime,0,sizeof(mintime));    
    cin>>n>>m;    
    for(int i=0;i<n;++i) cin>>speed[i];    
    sort(speed,speed+n,cmp);    
    for(int i=0;i<n;++i)     
    {   
        *min_element(mintime,mintime+m)+=speed[i];     
    }     
    cout<<*max_element(mintime,mintime+m)<<endl;   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wcxyky/article/details/88770452