贪心算法

一、贪心算法

       贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 [1]  。

       贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解

二、例题

例题一、区间问题

问题描述:

有n项工作,每项工作分别在si开始,ti结束。对每项工作,你都可以选择参加或不参加,但选择了参加某项工作就必须至始至终参加全程参与,即参与工作的时间段不能有重叠(即使开始的时间和结束的时间重叠都不行)。

限制条件:

1<=n<=100000
1<=si<=ti,=109

样例:

输入

5
1 2 4 6 8
3 5 7 9 10

输出
3(选择工作1, 3, 5)
题解:有三种算法:一是挑选开始时间最早的方案,二是挑选重叠次数最少的方案,三是结束时间最早的方案。经选择,选择方案三

代码:

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<string>
#include<utility>
using namespace std;
typedef pair<int ,int> P;
vector<P>vec;

bool cmp(P a,P b)//排序标准,优先根据结束时间早晚进行排序
{
    if(a.second==b.second)
        return a.first<b.first;
    return a.second<b.second;
}

int main()
{
    int n;//几组工作
    int edt,stt;//结束时间,开始时间
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&stt,&edt);
        vec.push_back(P(stt,edt));
    }
    sort(vec.begin(),vec.end(),cmp);
    int ans=1;//最多的工作,至少有第一个工作可以做,所以初始为1
              //第一个工作必选,因为结束时间最早
    int cm=vec[0].second;//当前最优工作,与下一个比较标准
    for(int i=1;i<n;i++)
    {
        if(vec[i].first>=cm)
        {
            ans++;
            cm=vec[i].second;
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

  


例题二、分饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值
样例

输入:3  2  
       1 2 3  
       1 1
输出: 1

题解:本题采用贪心策略。将饼干和孩子需要的从小到大排序,然后选择按顺序选择饼干。

代码

#include<iostream>
#include<string>
#include<algorithm>
#define maxn 100005
using namespace std;
int main()
{
    int N,M,cookie[maxn],stu[maxn];
    while(cin>>N>>M&&M)
    {
        int ans=0;
        for(int i=0;i<N;i++)
            cin>>cookie[i];
        for(int i=0;i<M;i++)
            cin>>stu[i];
        sort(cookie,cookie+N);
        sort(stu,stu+M);
        for(int c=0,s=0;s<M&&c<N;c++)
        {
            if(stu[s]>=cookie[c])
            {
                ans++;
                s++;
            }
            if(stu[s]<cookie[c])
               continue;
        }
        cout<<ans<<endl;
    }
    return 0;
}
例题三、字典序最小的问题Best Cow Line

FJ is about to take his N (1 ≤ N ≤ 2,000) cows to the annual”Farmer of the Year” competition. In this contest every farmer arranges his cows in a line and herds them past the judges.
The contest organizers adopted a new registration scheme this year: simply register the initial letter of every cow in the order they will appear (i.e., If FJ takes Bessie, Sylvia, and Dora in that order he just registers BSD). After the registration phase ends, every group is judged in increasing lexicographic order according to the string of the initials of the cows’ names.
FJ is very busy this year and has to hurry back to his farm, so he wants to be judged as early as possible. He decides to rearrange his cows, who have already lined up, before registering them.
FJ marks a location for a new line of the competing cows. He then proceeds to marshal the cows from the old line to the new one by repeatedly sending either the first or last cow in the (remainder of the) original line to the end of the new line. When he’s finished, FJ takes his cows for registration in this new order.
Given the initial order of his cows, determine the least lexicographic string of initials he can make this way.

输入
contains a single initial (‘A’…’Z’) of the cow in the ith position in the original line
输出
The least lexicographic string he can make. Every line (except perhaps the last one) contains the initials of 80 cows (‘A’…’Z’) in the new line.

样例
input
ACDBCB
output
ABCBCD
题解:字典序比较问题经常用到贪心

代码:

 

#include<cstring>
#include<cstdio>
#include<iostream>
#define maxn 30010
using namespace std;
int main()
{
    char c,ch[maxn];//吸收换行字符,输入顺序字符串
    char ans[maxn];
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%c%c",&c,&ch[i]);
    int left=0,right=n-1;//比较最左侧和最右侧字符大小
    int k=0;//最终答案串的序号
    while(left<right)
    {
        int i;//便于后续处理CCC这种情况
        for(i=0;i+left<=right-i;i++)
        {
            if(ch[i+left]>ch[right-i])
            {
                ans[k++]=ch[right];
                right--;
                break;
            }
            else if(ch[i+left]<ch[right-i])
            {
                ans[k++]=ch[left];
                left++;
                break;
            }
        }
        if(i+i>right-left)
        {
            ans[k++]=ch[left];
            left++;
        }
    }
    if(left==right)
       ans[k++]=ch[left];//增添最后一个条件,奇数增添,偶数不添
    ans[k]=0;
    int newl=1;//行数
    for(int i=0;i<n;i++)
    {
        if(i==newl*80)
        {
            newl++;
            printf("\n");
        }
        printf("%c",ans[i]);
    }
    return 0;
}

 

 


例题四、Saruman's Army

Description

Saruman the White must lead his army along a straight path from Isengard to Helm’s Deep. To keep track of his forces, Saruman distributes seeing stones, known as palantirs, among the troops. Each palantir has a maximum effective range of R units, and must be carried by some troop in the army (i.e., palantirs are not allowed to “free float” in mid-air). Help Saruman take control of Middle Earth by determining the minimum number of palantirs needed for Saruman to ensure that each of his minions is within R units of some palantir.

Input

The input test file will contain multiple cases. Each test case begins with a single line containing an integer R, the maximum effective range of all palantirs (where 0 ≤ R ≤ 1000), and an integer n, the number of troops in Saruman’s army (where 1 ≤ n ≤ 1000). The next line contains n integers, indicating the positions x1, …, xn of each troop (where 0 ≤ xi ≤ 1000). The end-of-file is marked by a test case with R = n= −1.

Output

For each test case, print a single integer indicating the minimum number of palantirs needed.

Sample Input

0 3
10 20 20
10 7
70 30 1 7 15 20 50
-1 -1

Sample Output
2
4

题意:在一条直线上,有n个点。从这n个点中选择若干个,给他们加上标记。对于每一个点,其距离为R以内的区域里必须有一个被标记的点。问至少要有多少点被加上标记。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 1100
using namespace std;
int main()
{
    int army[maxn];
    int r,num_army;
    while(scanf("%d%d",&r,&num_army)!=EOF&&r!=-1)
    {
        for(int i=0;i<num_army;i++)
            scanf("%d",&army[i]);
        sort(army,army+num_army);
        int ans=0;//最终结果
        int st=0;//循环到的序号
        while(st<num_army)
        {
            ans++;
            int i;//所能扩展到的最大长度
            for(i=1;st+i<num_army;i++)//合法范围内
                if(army[st]+r<army[st+i])
                    break;
            st+=i-1;//寻找满足最左侧边界的中间点
            for(i=1;st+i<num_army;i++)//合法范围内
                if(army[st]+r<army[st+i])
                    break;
            st+=i-1;//寻找最右侧边界
            st++;//下一个标记点的最左侧
        }
        printf("%d\n",ans);
    }
    return 0;
}

 


例题五、Fence repair

题目:
农夫约翰为了修理栅栏,要将一块很长的木板分割成N块。准备切成的木板的长度为L1、L2、……、Ln. 未切割木板的长度恰好为切割木板的长度和。每次切断木板时,需要的开销为这块木板的长度。例如,长度为21的木板切割成5、8、8的三块木板。长为21的木板切割成13、8时,开销为21.再将长度为13的木板切割成长度5、8时,开销为13.于是合计开销为34。于是按题目要求将木板切割完,最小的开销是多少?

限制条件:
1<=N<=2000
0<=Li<=5000

输入样例:
3

8 5 8
输出样例:
34
题解:此题可以转化为求最优二叉树(最小权)的问题,那么问题可以转化为最优二叉树的最小权是多少。可以通过计算分支点(内点和树根)之和来计算最小权。

代码:
#include<iostream>
#include<algorithm>
#include<string>
#define maxn 20010
using namespace std;
typedef long long int ll;
int N,a[maxn];
ll solve()
{
    ll ans=0;
    int minn,minn2;
    while(N>1)
    {
        sort(a,a+N,greater<int>());
        minn=a[N-1];
        minn2=a[N-2];
        int k=minn+minn2;
        ans+=k;
        a[N-2]=k;
        N--;
    }
    return ans;
}
int main()
{
    while(cin>>N&&N)
    {
        for(int i=0;i<N;i++)
            cin>>a[i];
        ll ans=solve();
        cout<<ans<<endl;
    }
    return 0;
}

posted @ 2020-07-23 21:54  Joelin12  阅读(318)  评论(0)    收藏  举报