貪心

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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章