贪心水题题解

这里是三道贪心的水题。


营养膳食

题目描述

    阿月正在女朋友宁宁的监督下完成自己的增肥计划。
    为了增肥,阿月希望吃到更多的脂肪。然而也不能只吃高脂肪食品,那样的话就会导致缺少其他营养。阿月通过研究发现:真正的营养膳食规定某类食品不宜一次性吃超过若干份。
    比如就一顿饭来说,肉类不宜吃超过 1 份,鱼类不宜吃超过 1 份,蛋类不宜吃超过 1 份,蔬菜类不宜吃超过 2 份。阿月想要在营养膳食的情况下吃到更多的脂肪,当然阿月的食量也是有限的。 
    输入
    输入第一行包含三个正整数 n(n≤200),m(m≤100)和k(k≤100)。表示阿月每顿饭最多可以吃 m 份食品,同时有 n 种食品供阿月选择,而这 n 种食品分为 k 类。
    第二行包含 k 个不超过 10 的正整数,表示可以吃 1 到 k 类食品的最大份数。接下来 n 行每行包括 2 个正整数,分别表示该食品的脂肪指数 ai 和所属的类别 bi,其中 ai≤100,bi≤k。 
    输出
    输出一个数字即阿月可以吃到的最大脂肪指数和。

样例输入

    6 6 3
    3 3 2
    15 1
    15 2
    10 2
    15 2
    10 2
    5 3

样例输出

    60

解析:

    贪心的题总会出现“更多”,“最多”等字眼(不过好像每道题都是这样)。在大概读完题之后,如果觉得能用贪心解决,那就想一个贪心。如果能举出反例,就再想一个贪心方法,如果最后反数据多得让你发现这道题不能再用贪心了,那正解可能是dp什么的。其实考试题不能轻易出贪心的,即使是贪心题也是得让你看不出来他是贪心,或者贪心的方法特别难。
    本道题的最优解是:按脂肪排序。每次吃脂肪最多的食物,如果当前食物的种类已经吃到上限了,那就不吃这个食物。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,k;
int lim[105];
int ans;
struct food
{
    int fat;
    int kk;    
};
bool cmp(food a,food b)
{
    return a.fat > b.fat;
}
food num[205];
int main()
{
//    freopen("diet.in","r",stdin);
//    freopen("diet.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
        scanf("%d",&lim[i]);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&num[i].fat,&num[i].kk);

    sort(num+1,num+n+1,cmp);

    for(int i=1;i<=m;i++)
    {    
        if(lim[num[i].kk] > 0)
        {
            ans += num[i].fat;
            lim[num[i].kk] -= 1;
        }
    }
    printf("%d",ans);
    return 0;
}

整数区间

题目描述

    一个整数区间[A,B]
    请编程完成以下任务:
    1.从文件中读取区间的个数及其它们的描述;
    2.找到满足下述条件的所含元素个数最少的集合中元素的个数,对于每一个区间,都至少
    有两个不同的整数属于该集合。 

输入

    首行包括区间的数目 n,1<=n<=10000,接下来的 n 行,每行包括两个整数 a,b,被一空格隔开,0<=a<=b<=10000,它们是某一个区间的开始值和结束值。 

输出

    第一行集合元素的个数,对于每一个区间都至少有两个不同的整数属于该区间,且集合所包含元素数目最少。 

输入

    4
    3 6
    2 4
    0 2
    4 7

输出

    4

解释一下:

可能题面描述的不太好,我解释一下。就是找一些最少的数,使每个区间至少包含这些数中的两个数,输出数的个数。

我的代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int cc[10005];
int ans,gg;
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    int l,r,n,mini=0x7fffffff,maxi=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&l,&r);
        for(int j=l;j<=r;j++)
        {
            cc[j]++;
        }
        mini = min(l,mini);
        maxi = max(r,maxi);
    }
    sort(cc+mini,cc+maxi,cmp);
    for(int i=mini;i<=maxi;i++)
    {
        ans += cc[i];
        gg++;
        if(ans >= 2*n)
            break;
    }
    printf("%d",gg);
    return 0;
}
/*
4
3 6
2 4
0 2
4 7
*/

解析:

    要不怎么说这道题数据水。我的方法绝对有瑕疵。
      我是这么干的。取1到10000这个区间(整形数组,1~10000是下标)。每读入一个区间,就把它覆盖上(就是这个数组每个下标存的数+1),这样把这些区间捏到一起,再将这个覆盖后的数组降序排序,然后从大到小一直累加加加,一直加到比区间个数n的二倍大,输出累加的这些数的个数。
    这个方法太土了,我都不知道怎么证他是对的这道题就a了。所以一定是数据太水了。我给一个正解代码吧。

正解代码:

#include<iostream>  
#include<algorithm>   
using namespace std;  
struct node  
{  
    int l;  
    int r;  
}a[10001];  
bool cmp(node a,node b)  
{  
    return (a.r<b.r||a.r==b.r&&a.l<b.l);  
}  
int main()  
{  
    int n;cin>>n;  
    for(int i=1;i<=n;i++)  
    cin>>a[i].l>>a[i].r;  
    sort(a+1,a+n+1,cmp);  
    int q[10001],front=2,back=1; 
    q[1]=a[1].r-1;q[2]=a[1].r; 
    for(int i=2;i<=n;i++)  
    { 
        int k=back; 
        while(a[i].l>q[back]&&back<k+2) 
          back++;

        if(back==k+1) q[++front]=a[i].r;  
        if(back==k+2) q[++front]=a[i].r-1,q[++front]=a[i].r;  

    } 
    cout<<front; 
}   
#include <stdio.h>

#define maxN 10005//最大边条数
#define inf 0x7fffffff//最大距离

struct Edge 
{
    int v, w, next;
}edge[4 * maxN];//边集

int dis[maxN];
bool vis[maxN];
int queue[maxN];
int preEdge[maxN];//同一个顶点的前一条边

int edgeNum,n,maxn;

void addEdge(int u, int v, int w)//添加一条边
{
    edge[edgeNum].v = v;
    edge[edgeNum].w = w;
    edge[edgeNum].next = preEdge[u];
    preEdge[u] = edgeNum ++;
}

void spfa()//spaf算法
{
    int head = 0, tail = 1;
    for (int i = 0; i <= maxn; ++ i)
    {
        dis[i] = -inf;
    }
    queue[head] = 0;
    dis[0] = 0;
    while (head != tail)
    {
        int u = queue[head];
        vis[u] = true;
        for (int p = preEdge[u]; p != 0; p = edge[p].next)
        {
            int v = edge[p].v;
            if (dis[v] < dis[u] + edge[p].w)
            {
                dis[v] = dis[u] + edge[p].w;
                if (!vis[v])
                {
                    vis[v] = true;
                    queue[tail] = v;
                    tail ++;
                    if (tail == maxN)
                    {
                        tail = 0;
                    }
                }
            }
        }
        vis[u] = false;
        head ++;
        if (head == maxN)
        {
            head = 0;
        }
    }
}

int main()
{
    scanf("%d", &n);
    edgeNum = 1;
    maxn = 0;
    while (n --)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        if (v + 1 > maxn)
        {
            maxn = v + 1;
        }
        addEdge(u, v + 1, 2);
    }
    for (int i = 0; i <= maxn; ++ i)
    {
        addEdge(i, i + 1, 0);
        addEdge(i + 1, i, -1);
    }
    spfa();
    printf("%d
", dis[maxn]);
    return 0;
View Code

运输

题目描述

    现在已知 N 件商品,和搬运它们其中每一件的费用。现在搬家公司老板 Mr.sb 决定让我们每次任意选取 2 件商品。然后这 2 件商品只算一件商品的费用。但是这个商品的搬运费用是将选出的 2 个商品的费用之和除以 k 的运算结果。如此反复。直到只收一件商品的钱。这个就是商店要付的费用。掌柜的想尽可能的少付钱,以便将更多的钱捐给希望工程。所以请你帮他计算一下最少只用付多少钱。 

输入

    n,k
    w1,w2,...,wn(每一件商品的搬运费用)
    n,k<=10000

输出

    最少付多少钱

样例输入

    5 2
    1 2 3 4 5

样例输出

    1

解析:

    贪心策略就是取两个最大的数,除完k放回去,再取两个最大的,再放回去……因为贪心总是什么取最大啦,排序啦这个样子。用优先队列就比较方便。

代码:

#include<cstdio>
#include<algorithm>
#include<queue>
typedef long long ll;
using namespace std;
int n,k,hop;
priority_queue <int> q;
int main()
{
//    freopen("trans.in","r",stdin);
//    freopen("trans.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&hop);
        q.push(hop);
    }
    while(n>1)
    {
        hop = q.top();
        q.pop();
        hop += q.top();
        q.pop();
        hop /= k;
        q.push(hop);
        n--;
    }
    printf("%d",hop);
    return 0;
}
原文地址:https://www.cnblogs.com/Ch-someone/p/8593264.html