贪心

1.区间调度问题

n项工作,每项工作都在s[i]时开始,在e[i]时结束,对于每项目工作都可以选择是否参加,如果选择了参与,那么自始至终都必须全程参与,参加工作时间段不能重叠,求最多能参加多少数量工作

样例:

n=5

s=1,2,4,6,8  e=3,5,7,9,10

result=3

思路:在可选择的工作中,每次都选取结束时间最早的工作

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
using namespace std;
typedef long long LL;
const int MAX_N=1000;
int n;
int k;
int s[MAX_N];//开始时间
int e[MAX_N];//结束时间
pair<int,int>it[MAX_N];
int solve()
{
    //让时间结束早的工作排前面,e存在前面,s存在后面
    for(int i=0;i<n;i++)
    {
        it[i].first=e[i];
        it[i].second=s[i];
    }
    stable_sort(it,it+n);//按照字典序排列
    int ans=0,t=0;
    for(int i=0;i<n;i++)
    {
        if(t<it[i].second)//将上一个结束的时间与下一个开始的时间对比
        {
            ans++;
            t=it[i].first;
        }
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {

        for(int i=0; i<n; i++)
        {
            cin>>s[i];

        }
        for(int i=0; i<n; i++)
        {
            cin>>e[i];

        }
        cout<<solve()<<endl;
    }

    return 0;
}

poj3617点击打开链接

题意:给定长度为n的字符串,构造一个长度为n的字符串,将s的头部和尾部进行比较,要求构建出的字符串尽可能小

题解:参考编程挑战,利用贪心思想,将首尾元素进行对比,将小的元素进行输出,如果元素相同,就接着往下比较

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
using namespace std;
typedef long long LL;
const int MAX_N=2005;
char s[MAX_N];
int main()
{
    int n;
    int cnt;
    while(scanf("%d",&n)!=EOF)
    {
        memset(s,0,sizeof(s));
        cnt=0;
        for(int i=0; i<n; i++)
        {
            scanf(" %c",&s[i]);
        }
        int a=0;
        int b=n-1;
        while(a<=b)
        {
            bool left=false;
            for(int i=0;a+i<=b;i++)
            {
                if(s[a+i]<s[b-i])
                {
                    left=true;
                    break;
                }
                else if(s[a+i]>s[b-i])
                {
                    left=false;
                    break;
                }
            }
            if(left)
            {
                putchar(s[a++]);
            }
            else
            {
                putchar(s[b--]);
            }
            cnt++;
            if(cnt==80)
            {
                printf("\n");
                cnt=0;
            }

        }
        printf("\n");

    }
    return 0;
}

poj3069点击打开链接

题解:从最左边开始考虑,寻找最远的点,将其标记,之后类推寻找最远点

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
using namespace std;
typedef long long LL;
const int MAX_N=2005;
int a[MAX_N];
int n,r;
int solve()
{
    sort(a,a+n);
    int i=0;
    int ans=0;
    while(i<n)
    {
        int s=a[i++];

        //向右寻找距离包含在范围内的最远点
        while(i<n&&a[i]<=s+r)
        {
            i++;
        }
        int p=a[i-1];

        //一直向右寻找距离超出范围的点
        while(i<n&&a[i]<=p+r)
        {
            i++;
        }
        ans++;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&r,&n))
    {
        if(r==-1&&n==-1)break;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        cout<<solve()<<endl;
    }
    return 0;
}

木板切块问题  poj3253点击打开链接

题意:例如长度为21的木板要切成5,8,8的三块木板。首先,长度21的木板切成13,8开销为21,再将长度为13的木板切成长度为5,8需要开销13,求最小开销

题解:贪心+哈夫曼树,设木板长度为L1,L2,L3...Ln,将其构建为哈夫曼树即最有二叉树,二叉树的根节点相加之和为最优解

首先介绍一个STL模板,priority_queue,引用一下前辈的博客http://www.cnblogs.com/flyoung2008/articles/2136485.html

接下来就是题目详解代码,采用了两种方法,推荐第二种,第一种超时了,思路犹存

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
using namespace std;
typedef long long LL;
const int MAX_N=20005;
int a[MAX_N];
int n;
int cmp(int u,int v)
{
    return u>v;
}
LL solve1()
{
    memset(a,0,sizeof(a));
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    int k=n;
    LL ans=0;
    while(k>1)
    {
        stable_sort(a,a+k,cmp);


        a[k-2]=a[k-1]+a[k-2];
        ans+=a[k-2];
        k--;

    }
    return ans;
}
LL solve2()
{
    memset(a,0,sizeof(a));
    LL ans=0;
    int u,v;
    priority_queue<int,vector<int>,greater<int> >que;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        que.push(a[i]);
    }
    while(que.size()>1)
    {
        u=que.top();
        que.pop();
        v=que.top();
        que.pop();
        ans+=(u+v);
        que.push(u+v);
    }
    que.pop();
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        cout<<solve2()<<endl;
    }
    return 0;
}

关于优先队列的模板,这里再补充一道同样是运用贪心+优先队列的题目

题目链接如下https://vjudge.net/problem/POJ-2431

题意:给出一个数n,代表在两所城镇之间有n个加油站,接下来给出n行代表每个加油站距离目标城镇的距离以及所能提供的最大加油量,最后给出最开始汽车含有多少油,以及两所城镇的距离,假设卡车的燃油量是无限的,求卡车能否到达终点,如果可以,求出最少加油次数,否则输出-1

题解:不妨假设汽车所携带的油走过最长路径,将最长路径里的加油站所能加的油最大值加入到汽车中,不断维护这个优先队列,直至到达终点

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
const int MAX_N=10010;
typedef long long ll;
typedef long long LL;
int n,l,pp;
struct stu
{
    int a;
    int b;
} p[MAX_N];
bool cmp(stu u,stu v)
{
    return u.a<v.a;
}
int solve()
{
    priority_queue<int>q;
    int ans=pp;//剩余油
    int res=0;//加油次数
    int s=0;//初始位置
    int d;//需要跑多少
    p[n].a=l;
    p[n].b=0;
    for(int i=0; i<=n; i++)
    {
        int d=p[i].a-s;
        while(ans-d<0)//剩下的油不足以支撑下一个加油站
        {
            if(q.empty())
            {
                return -1;
            }
            ans+=q.top();
            res++;//加1次油
            q.pop();
        }
        ans-=d;
        s=p[i].a;
        q.push(p[i].b);
    }
    return res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {

        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&p[i].a,&p[i].b);
        }

        scanf("%d%d",&l,&pp);
        for(int i=0; i<n; i++)
        {

            p[i].a=l-p[i].a;
        }
        sort(p,p+n,cmp);
        printf("%d\n",solve());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值