《程序设计与算法》之【贪心算法】

44 篇文章 1 订阅

圣诞老人的礼物(百练4110)

题目描述
圣诞节来临了,圣诞老人准备分发糖果,现 在有多箱不同的糖果,每箱糖果有自己的价值和重 量,每箱糖果都可以拆分成任意散装组合带走。圣 诞老人的驯鹿雪橇最多只能装下重量W的糖果,请 问圣诞老人最多能带走多大价值的糖果。

输入:
第一行由两个部分组成,分别为糖果箱数正整数n(1 <= n <= 100),驯鹿能承受的最大重量正整数w(0 < w < 10000),两个数用空格隔开。其余n行每行对应一箱糖 果,由两部分组成,分别为一箱糖果的价值正整数v和重 量正整数w,中间用空格隔开。

输出:
输出圣诞老人能带走的糖果的最大总价值,保留1位小数 。输出为一行,以换行符结束。

样例输入:
4 15
100 4
412 8
266 7
591 2

样例输出:
1193.0

解题思路
按礼物的价值/重量比从大到小依次选取礼物,对选 取的礼物尽可能多地装,直到达到总重量w,复杂度: O(nlogn)

解答

#include <iostream>
using namespace std;
const double eps = 1e-6;
struct Candy {
    int v;
    int w;
    bool operator < (const Candy & c) const {
        return double(v)/w - double(c.v)/c.w > eps;
    }
} candies[110];

int main() {
    int n, w;
    scanf("%d%d", &n, &w);
    for (int i = 0; i < n; i++) {
        scanf("%d%d", &candies[i].v, &candies[i].w);
    }
    sort(candies, candies+n, cmp);
    int totalW = 0;
    double totalV = 0;
    for (int i = 0; i < n; i++) {
        if (totalW + candies[i].w <= w) {
            totalW += candies[i].w;
            totalV += candies[i].v;
        } else {
            totalV += candies[i].v * double(w-totalW)/candies[i].w;
            break;
        }
    }
    printf("%.1f\n", totalV);
    return 0;
}

每一步行动总是按某种指标选取最优的操作来进行,
该指标只看眼前,并不考虑以后可能造成的影响。“圣诞老人礼物”题,若糖果只能整箱拿,则贪心法错误。

电影节(百练4151)

题目描述
大学生电影节在北大举办! 这天,在北大各地放了多部电影 ,给定每部电影的放映时间区间,区间重叠的电影不可能同时 看(端点可以重合),问李雷最多可以看多少部电影。
输入:
多组数据。每组数据开头是n(n<=100),表示共n场电影。接下来n行,每行两个整数(均小于1000),表示一场电影的放映区间,n=0则数据结束

输出:
对每组数据输出最多能看几部电影。

Sample Input12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0

Sample Output:
5

解题思路
将所有电影按结束时间从小到大排序,第一步选结束时间最早的那部电影。 然后,每步都选和上一部选中的电影不冲突且结束时间最早的电影。
复杂度: O(nlogn)

#include <iostream>
using namespace std;
struct Film {
    int b;
    int e;
    bool operatoc < (const Film & f) const {
    return e < f.e;
}
} films[110];

int main() {
    int n;
    while (scanf("%d", &n) && n != 0) {
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &films[i].b, &films[i].e);
        }
        sort(films, films+n, cmp);
        int total = 1;
        int last = 0;
        for (int i = 1; i < n; i++) {
            if (films[i].b >= films[last].e) {
                last = i;
                total++;
            }
        }
        printf("%d\n", total);

    }
    return 0;
}

Stall Reservations(百练3190)

题目描述
有 n头牛(1<=n<=50,000)要挤奶。给定每头牛挤奶的时间区间[A,B] (1<=A<=B<=1,000,000,A,B为整数)。
牛需要呆畜栏里才能挤奶。一个畜栏同一时间只能容纳一头牛。 问至少需要多少个畜栏,才能完成全部挤奶工作,以及每头牛都放哪个畜栏里(Special judged)
去同一个畜栏的两头牛,它们挤奶时间区间哪怕只在端点重合也 是不可以的。

解题思路
贪心解法:
所有奶牛都必须挤奶。到了一个奶牛的挤奶开始时间,就必须为这个奶牛找畜栏。因此按照奶牛的开始时间逐个处理它们,是必然的。
S(x)表示奶牛x的开始时间。E(x)表示x的结束时间。对E(x), x可以是奶牛 ,也可以是畜栏。畜栏的结束时间,就是正在其里面挤奶的奶牛的结束时间。同一个畜栏的结束时间是不断在变的。
1) 把所有奶牛按开始时间从小到大排序。
2) 为第一头奶牛分配一个畜栏。
3) 依次处理后面每头奶牛i。处理 i 时,考虑已分配畜栏中,结束时间最早的畜栏x。
若 E(x) < S(i), 则不用分配新畜栏,i可进入x,并修改E(x)为E(i)
若 E(x) >= S(i),则分配新畜栏y,记 E(y) = E(i)
直到所有奶牛处理结束

需要用优先队列存放已经分配的畜栏,并使得结束时间最早的畜栏始终 位于队列头部。
复杂度: O(nlogn)

解答

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
struct Cow {
    int b, e; // 挤奶区间起点和终点
    int No;   // 编号
    bool operator < (const Cow & c) const {
        return b < c.b;
    }
} cows[50100];

int pos[50100];   // pos[i]表示编号为i的奶牛去的畜栏编号
struct Stall {
    int end;   // 结束时间
    int No;    // 编号
    bool operator < (const Stall & s) const {
        return end > s.end;
    }
    Stall (int e, int n) : end(e), No(n) {}
};
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d", &cows[i].b, &cows[i].e);
        cows[i].No = i;
    }
    sort(cows, cows+n);
    int total = 0;
    priority_queue<Stall> pq;
    for (int i = 0; i < n; i++) {
        if (pq.empty()) {
            ++total;
            pq.push(Stall(cows[i].e, total));
            pos[cows[i].No] = total;
        } else {
            Stall st = pq.top();
            if (st.end < cows[i].b) {   // 端点也不能重合
                pq.pop();
                pos[cows[i].No] = st.No;
                pq.push(Stall(cows[i].e, st.No));
            } else {
                ++total;
                pq.push(Stall(cows[i].e, total));
                pos[cows[i].No] = total;
            }
        }
    }
    printf("%d\n", total);
    for (int i = 0; i < n; i++) {
        printf("%d\n", pos[i]);
    }
    return 0;
}

Radar Installation(百练1328)

题目描述
x轴是海岸线,x轴上方是海洋。海洋中有n (1<=n<=1000)个岛屿,可以看作点。
给定每个岛屿的坐标(x,y),x,y 都是整数。当一个雷达(可以看作点)到岛屿的距离 不超过d(整数),则认为该雷达覆盖了该岛屿。雷达只能放在x轴上。问至少需要多少个雷达才可以覆盖全部岛屿。

输入:
The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases.
The input is terminated by a line containing pair of zeros

输出:
For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. “-1” installation means no solution for that case.

样例输入:
3 2
1 2
-3 1
2 1

1 2
0 2

0 0

样例输出:
Case 1: 2
Case 2: 1

解答思路
对每个岛屿P,可以算出,覆盖它的雷达,必须位于x轴上的区间[Ps ,Pe ]。
如果有雷达位于某个x轴区间 [a,b],称该雷达覆盖此区间。问题转换为,至少要在x轴上放几个雷达(点),才能覆盖全部区间[P1s ,P1e ],[P2s ,P2e ]….[Pns ,Pne ]

如果可以找到一个雷达同时覆盖多个区间,那么把这多个区间按起点坐标从小到大排序 ,则最后一个区间(起点最靠右的)k的起点,就能覆盖所有区间。

证明:如果它不能覆盖某个区间x,那么它必然位于 1) x起点的左边 ,或者2)x终点的右边。
情况1) 和 k 的起点是最靠右的矛盾
情况2) 如果发生,则不可能找到一个点同时覆盖x和k,也和前提矛盾
有了这个结论,就可以只挑区间的起点来放置雷达了。

贪心算法:
1 ) 将所有区间按照起点从小到大排序,并编号0 - (n-1)
2 ) 依次考察每个区间的起点,看要不要在那里放雷达。开始,所有区间都没被覆盖,所以目前编号最小的未被覆盖的区间的编号 firstNoConverd = 0

3 ) 考察一个区间 i 的起点 xi 的时候,要看从 firstNoCovered 到区间i1 中是否存在某个区间 c , 没有被 xi 覆盖。如果没有,则先不急于在xi 放雷达,接着往下看。如果有,那么 c 的终点肯定在xi 的左边,因此不可能用同一个雷达覆盖 c 和i。即能覆盖c的点,已经不可能覆盖i和i后面的区间了。此时,为了覆盖c,必须放一个雷达了,放在区间 i-1 的起点即 可覆盖所有从firstNoCovered到 i-1的区间。因为当初考察 i-1的起点 z 时候,并没有发现 z 漏覆盖了从 firstNoCovered 到 i-2 之间的任何一 个区间。
4) 放完雷达后,将 firstNoCovered改为i,再做下去。
复杂度 : O(n^2 )

#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
struct Island {
    int x1, x2;
    Island(int l, int r): x1(l), x2(r) {}
    bool operator < (const Island& i) const {
        return x1 > i.x1;
    }
};
int main() {
    int n, d;
    int t = 0, ans;
    while (scanf("%d%d", &n, &d) && (n || d)) {
        t++;
        bool flag = true;
        ans = 1;
        priority_queue<Island> pq;
        for (int i = 0; i < n; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (abs(y) > d)
                flag = false;
            else
                pq.push(Island(x-sqrt(d*d-y*y), x+sqrt(d*d-y*y)));
        }
        if (flag == false)
            ans = -1;
        else {
            Island p = pq.top();
            pq.pop();
            while (!pq.empty()) {
                Island tmp = pq.top();
                pq.pop();
                if (tmp.x1 > p.x2) {
                    ans++;
                    p = tmp;
                } else if (tmp.x2 < p.x2) {
                    p = tmp;
                }
            }
        }
        printf("Case %d: %d\n", t, ans);
    }
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C语言是一种计算机编程语言,可以使用它来实现屏幕飘雪的效果,这在制作冬季主题的计算机界面时很常见。 圣诞树是圣诞节庆祝活动常见的装饰物品之一,通常是一棵用彩灯和其他装饰品装饰的人工或真树。 圣诞老人圣诞节传统文化的一个角色,传说他会穿着红色的衣服和白色的胡子,驾着驯鹿拉的雪橇,给孩子们送礼物。 圣诞音乐是一种特殊的音乐类型,通常与圣诞节庆祝活动相关,包括圣诞颂歌、圣诞卡罗尔等,常用于增强节日气氛。 ### 回答2: C语言是一种高级编程语言,可以用来编写各种应用程序。在C语言,我们可以使用一些函数和库来实现一些有趣的效果,比如屏幕飘雪。 屏幕飘雪是指在计算机屏幕上显示出雪花飘落的效果。我们可以使用C语言的图形库,比如graphics.h,来绘制雪花的图案和动画效果,通过控制雪花的位置和速度,实现屏幕飘雪的效果。 圣诞树是圣诞节的象征之一,代表着家庭和谐、幸福和团聚。在C语言,我们可以使用字符和循环语句来打印出一个圣诞树的形状,其最底下的一排代表树干,上面的几排代表树枝和叶子,可以使用不同的字符来表示不同的部分,比如“*”表示叶子,“|”表示树干,通过合理的排列和循环控制,就可以打印出一个漂亮的圣诞树。 圣诞老人圣诞节的象征之一,传说他会在圣诞夜来到孩子们的家里,给他们送礼物。在C语言,我们可以使用图形库或者字符来绘制圣诞老人的图案,比如使用圆和直线来绘制头、身体和腿部,使用圆来表示脸部,通过合理控制形状和颜色,可以绘制出一个可爱的圣诞老人形象。 圣诞音乐是圣诞节的重要组成部分,给人们带来了欢乐和温馨的氛围。在C语言,我们可以使用音乐库或者简单的音频播放函数来播放圣诞音乐。通过引入音频文件或者直接使用C语言提供的音乐函数,我们可以在程序添加适当的代码来播放圣诞音乐,让人们在编程的同时享受到美妙的音乐。 ### 回答3: C语言是一种高级编程语言,可以用来创建各种应用程序。在使用C语言编写程序时,我们可以实现屏幕飘雪效果。通过在屏幕上绘制雪花的图案,并让它们以某种方式移动,我们可以模拟下雪的场景。这种效果常在圣诞节期间应用,可以增添节日气氛。 圣诞树是圣诞节的象征之一,它象征着平安、团圆和希望。我们可以使用C语言编写程序来绘制一个圣诞树的图案。通过绘制的技巧,我们可以用字符组成树的形状,并添加彩灯的效果,使得树看起来更加美丽。 圣诞老人圣诞节的象征之一,他是给孩子们送礼物的好心人。我们可以使用C语言编写程序来模拟圣诞老人的动作。通过使用C语言的动画绘制技术,我们可以让圣诞老人在屏幕上移动和交互,给人们带来欢乐和惊喜。 圣诞音乐是圣诞节庆祝活动的重要组成部分。我们可以使用C语言编写程序来播放圣诞音乐。通过利用C语言的音频处理库,我们可以将音乐文件解码,然后输出到音频设备,从而实现音乐的播放。可以选择一些经典的圣诞歌曲,如《铃儿响叮当》、《圣诞老人进城》等,让人们在欢庆节日的同时,聆听美妙的音乐。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值