贪心基础题总结

做了几道贪心基础例题,感觉代码不多,但好难想。。。下面附上几道题

①区间调度问题,每项工作分别在si,时间开始ti结束,不能重叠,问最多能参与多少项工作?(见《挑战》P40)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define maxn 10000

int n,s[maxn],t[maxn];
pair<int,int> itv[maxn];

void solve()
{
    for(int i=0;i<n;i++)
    {
        itv[i].first=t[i];
        itv[i].second=s[i];//把开始与结束时间捆绑
    }
    sort(itv,itv+n);

    int ans=0,t=0;//t是最后所选工作的结束时间
    for(int i=0;i<n;i++)
    {
        if(t<itv[i].second)//结束时间小于开始时间
        {
            ans++;
            t=itv[i].first;
        }
    }
    printf("%d\n",ans);
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&s[i]);
    for(int i=0;i<n;i++)
        scanf("%d",&t[i]);

    solve();
   return 0;
}

②硬币问题,用有限张数的不同面值的纸币支付A原,问最少需要几张?

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;

const int v[6]={1,5,10,50,100,500};
int c[6];//c[0]=c1,c[1]=c2....
int A;

void solve()
{
    int ans=0;
    for(int i=5;i>=0;i--)
    {
        int t=min(A/v[i],c[i]);//必须满足纸币的个数
        A-=t*v[i];
        ans+=t;
    }
    printf("%d\n",ans);
}

int main()
{
    scanf("%d",&A);
    for(int i=0;i<6;i++)
    scanf("%d",&c[i]);
    solve();
    return 0;
}

③贪心算法——字典序最小问题,见《挑战》P43

 

问题主题:字典序最小

问题描述:

给定长度为N的字符串S,要构造一个长度为N字符串T。T是一个空串,反复执行下列任意操作:

l 从S的头部删除一个字符,加到T的尾部;

l 从S的尾部删除一个字符,加到T的尾部;

目标是要构造字典序尽可能小的字符串T。

限制条件:

1<=N<=2000

字符串都在大写字符

样例:

①输入

N=8

ADHCACBD

输出

②输入

N=5

ADCDA

输出

AADCD

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define maxn 2000
int n;
char s[maxn];

void solve()
{
    int a=0,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--]);
    }
    putchar('\n');
}

int main()
{
   scanf("%d",&n);
   scanf("%s",s);
   solve();
   return 0;
}

④这个题是说给你n个点,然后让你标记其中尽可能少的点,使得n个点都处于被标记点左右不超过R的区间内,,求至少标记多少点?

思路:贪心~尽量充分利用区间,见《挑战》P45

输入:N=6

      R=10

      X={1,7,15,20,30,50}

输出:3

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1000

int x[maxn];
int N,R;

void solve()
{ int i=0,ans=0;
    sort(x,x+N);
    while(i<N)
    {
        int s=x[i++];
        //一直向右前进直到距离s的点大于R
        while(i<N&&x[i]<=s+R) i++;

        int p=x[i-1];//P是新加上的标记点的位置,实质将S更新为P
        while(i<N&&x[i]<=p+R) i++;
        ans++;
    }
    printf("%d\n",ans);
}

int main()
{
    scanf("%d%d",&N,&R);
    for(int i=0;i<N;i++)
        scanf("%d",&x[i]);
    solve();
    return 0;

}

⑤.POJ3253 Fence repair见《挑战》P47

有一个农夫要把一个木板钜成几块给定长度的小木板,每次锯都要收取一定费用,这个费用就是当前锯的这个木版的长度

给定各个要求的小木板的长度,及小木板的个数n,求最小费用

 

提示:

3

5 8 5为例:

先从无限长的木板上锯下长度为 21 的木板,花费 21

再从长度为21的木板上锯下长度为5的木板,花费5

再从长度为16的木板上锯下长度为8的木板,花费8

总花费 = 21+5+8 =34

5

1 2 3 4 5

33

下面用的是优先队列,贪心可能超时

#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 2000
typedef long long ll;

int N;
int L[maxn],s[maxn];

void solve()
{
    ll ans=0;
    //声明一个从小到大取出数值的优先队列
    priority_queue<int,vector<int>,greater<int> > que;
     for(int i=0;i<N;i++)
     {
         que.push(L[i]);
     }
     //不断地进行木板合并
     while(que.size()>1)
     {
         int l1,l2;
      l1=que.top();
        que.pop();
      l2=que.top();
        que.pop();
         ans+=l1+l2;
         que.push(l1+l2);
     }
     printf("%lld\n",ans);
}


int main()
{
    scanf("%d",&N);
    for(int i=0;i<N;i++)
        scanf("%d",&L[i]);
    solve();
    return 0;
}

点赞